From 10616dab388f824db0e949ce6bde6472b1e64868 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Thu, 2 Sep 2010 00:44:11 +0000 Subject: [PATCH] Applied jmimu's patch which shows players affected by attachments and explosion in the player icon list, and 'animates' if a kart is overtaken another kart. Thanks a lot for this patch! git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@5856 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/models/anchor-attach-icon.png | Bin 0 -> 3217 bytes data/models/bomb-attach-icon.png | Bin 0 -> 11821 bytes data/models/materials.xml | 3 + data/models/parachute-attach-icon.png | Bin 0 -> 18331 bytes src/items/attachment_manager.cpp | 19 +- src/items/attachment_manager.hpp | 5 + src/karts/emergency_animation.hpp | 12 +- src/karts/kart.hpp | 9 +- src/modes/linear_world.cpp | 13 +- src/modes/linear_world.hpp | 7 +- src/states_screens/race_gui.cpp | 368 +++++++++++++++++++------- src/states_screens/race_gui.hpp | 6 + 12 files changed, 335 insertions(+), 107 deletions(-) create mode 100644 data/models/anchor-attach-icon.png create mode 100644 data/models/bomb-attach-icon.png create mode 100644 data/models/parachute-attach-icon.png diff --git a/data/models/anchor-attach-icon.png b/data/models/anchor-attach-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9c85921367cfab743833dbb8aed849fad003ab6a GIT binary patch literal 3217 zcmV;C3~uv@P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igc8 z5;zZMMJVt901OdHL_t(|+U=Taj9le;$A8cJo^$59vp3tjGrP;;-Su9)Yn!zfjNODw zf@I_1nnrGlxJYf3Dk^C{m8fmp1X7YnNkLSEkn|>{3ShNy4M}Nel{Pg55Gf|u*sd@b ze8aoO*Y$dK=ggdQ-lrdy&>|KW6k^1B|I&PUXY^{G|9PJOb2~7HIm}@WbC|;%<}il~ z6ZXCQ__E%%WsyzSCMT!D*+PJM*T(miKI3pZmKIZ|t{7^+_JShdvfknbBm7O2KyQ;_ zV1ZXZTL|#Z_2ue;HmXkh6Q^4Rp)sT617#|4KfNG4;Hj?;gJY{TMryHRb-U5DZ-0K@ zzaKhwL5SdEJInXw0$CS$NY+z^78ogH{7+uq-7vas@A#}BKxR4|=`B%Bba{PBo@>op z-Sv7b*Dmrj>zil4{@B5lvndU}Ed;Jw(#H0eMyU(rr#($p_nt`P(l$@q3L*>Y{LlU6 zr+Q{p9q=|0+_viomk*SFv3R~MoT;cFFf?C@)IRWqf84iywiQBe@qkqW?OfVhx~Y_x z;gm&Q8CQl|!5AwWFKPV45Bkpz4GsPJHQ*xyZKp?CO@nXY$x&tF;uOaw-NA1>w*TVu zW$3C^tMcRHcI?=3a%gCX-Me?czaFrnw|MLPtZB%k47s#$ zRj0+X!V5>EyZbIKowp~g&MgUo;6Y=|A0=_ri^vAgk>XQFjym;}m3i+AtSkc@1qgdso!0`Bz?&9fvC*4f|kNoaMGZV0~ zzwNg6dV{e-+JZF@SRssrLr0E2c|K!ZRY4t2EJ+-b$z`NAprWv5p=r2ht^dH^eX9M{>%VtsW&(173^!#h%~^{TgE**# zicx*(e2ZNso3)5R;}{VkiXs5A*(}BwL;zI;1QZ8f>+kDTBXR>^V#j9zt*Oe!9*^dn zN2TT{hi2iHn_I7#k${XRE3$#nv?t^&w5Ba73*(`lJl_QD*|X>8sZ?s8@jOl97~XLG zD2lMwPJ07@24Uu1v zTt3fG3+$Yz(N!>1#3AW~YyNiZ=@|)V&Kf%EEf^t_wlwAq`IIo_&VRmt>Zzv~9v)`r z&YeAo>_J2Wqi8rmQaM9dKF!orh=>pb0V492ylyn196G-9QUT{@Sh+y7(KjUM9q(8P zPSz5l*phKzT*BEH65rsE<`1Mxn|%GcrCr63#b=Jz#E4)F4Jn~H4bBM@QI(U<;_%31 zGDJ~C9LG545CpLn(S)5Re5PV6H+D@vaMR||ubi_KE>AhM-V$dFo^#Yf#S?ndjjbKw z>7$3=ZzHc5D)Geb>GZm?uk>4C;y#)Rf>H?z~-$-*tsq>YLO(Zai z7(>P=7&J^oCL+tYW>i&$Btbk6eZ%N{K2Nn;C5mE_xQ4MF&N;zpUw?nU8e_VE{cjgR zWn3e`2`*8>DaXVFR3d1}o8PW)YAtkj6yKA1QWbrs zA+5K!eb68&ra-cilT}&M&dXkNTqSk zVXejUJgU_yVHlFhWa#ehrn9q?&d$y+_xASQxo+LM=@58g_Yr^xOLZOhFKe-NDIf7H zlq*V9OUMVvz&yKRs#d&x|KYK>2+3eq8+%?oGMz^|i`OM8H~U7f46L@!%L*+yr9Ler z4(ig7@gZ%6$r!!QmA;uWg7~(j_TFcrsYp8E%U~FuRTCK*^)YQxC)~$PMH@N1K z4iaVglKJMj_BtqKEtME16F3_?4vafqJf5)clWF?`wzznAvt%=%Ct zp!I3U`XYt2!!tsC3XHV`2F`{S-vcXxuMs&fA#!rChTJ_KnTNjm=;4#!*wDrw|M1AW z*Opb6|G`uxyx%#eAb8eDwHo64(;=|-+G`1`Rl=!BqDqsU|QUu~ie<^7)5HHv`xZIOnufY^8r-P^#6CbUICAW8?FiHf>sdP67Zn40oJfS~LywvO>y2 z)_NGvvG{0jsB~RB|f|5 z*WX%y{NvXY%H^~7wY0R{GBPr9JV}yokBp4mw|@QllXu;97q{JZ+qnrC>23eo(goh1 zE-hNg`ASlSz!#23P>G>FtyB_6J`nP@h85M6Rq-s;VtMHFBwIfBgVBcp?*C+oTepsW z5CtWwYDFY@Ph)X_PYicxcaukRURaP7CTogkp)O@e5Drd+v}Khfc6e5fhly;Cad&;; z2cw7i5MjpE*qWsmZ5(d#uokVxNF@T_z=E9Nbf}t3A=xN8c=oK^9h=nVO+PwtVpgrj z{_TZBTb_Kaw)~Yd$rK8naV*F~x$3A3R6JwWZTj)ii#;pf`Y;=R&JX~&a;RMg<{OqY z`eXw~5TMma<?c5Ap%|4|9O5a41Xbj4 z+4;L(IWqeR@Sw*xPCZXaRLS^;NDQLVcR_@J7@kQPOTj|M7gP*_)0E-r3xa@&NbZ`9 zk#ZzxGCeV^Mn+olw)Nun)+-lvmS#5r&%XHD;mH~k*0WkmET=+`OaO(nqo>i^_VU5e z*-gOO;SS2NyL!Chq^e3fP!b23z|z%Zb!k^=+iW3V#@FQb9i3qB=)_o0YeBQVT-}%v ze9sUk5IM-F%*CO}mQI~1KRPQ3_$6LAJn{JAw!#xx->eVNj3>e~qp_TrQtfH7BQ=}r zJ9Vb~PyGwqI5B>9b~Qu4M198m=bt=sp!syDcOE($ACH`2xa9NPXrgJ48+tm6`(E6C zgszScXMyyd>-xQ48oczmLz7=LqSrrh2t9Dd@%(7~^4Qo|->eIPnQ&E4ZuED)ci0@00000NkvXXu0mjf DGcz}K literal 0 HcmV?d00001 diff --git a/data/models/bomb-attach-icon.png b/data/models/bomb-attach-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b511acb995e91da0a5ad3101e27d59a79a08c33c GIT binary patch literal 11821 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 + + + diff --git a/data/models/parachute-attach-icon.png b/data/models/parachute-attach-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9baec0b427074bd8e37689c31b58d9f6606b4281 GIT binary patch literal 18331 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[i].file); m_attachments[iat[i].attachment]=irr_driver->getAnimatedMesh(full_path); + + std::string full_icon_path = file_manager->getModelFile(iat[i].icon_file); + m_all_icons[iat[i].attachment]=material_manager->getMaterial(full_icon_path, + /* full_path */ false, + /*make_permanent */ true); + } // for } // reInit diff --git a/src/items/attachment_manager.hpp b/src/items/attachment_manager.hpp index 9692340d9..d384db374 100644 --- a/src/items/attachment_manager.hpp +++ b/src/items/attachment_manager.hpp @@ -29,6 +29,7 @@ class AttachmentManager { private: scene::IAnimatedMesh *m_attachments[ATTACH_MAX]; + Material *m_all_icons [ATTACH_MAX]; public: AttachmentManager() {}; /** Returns the mest for a certain attachment. @@ -36,6 +37,10 @@ public: scene::IAnimatedMesh *getMesh(attachmentType type) const {return m_attachments[type]; } void removeTextures (); void loadModels (); + /** Returns the icon to display in the race gui if a kart + * has an attachment. */ + const Material* + getIcon (int type) const {return m_all_icons [type];} }; extern AttachmentManager *attachment_manager; diff --git a/src/karts/emergency_animation.hpp b/src/karts/emergency_animation.hpp index 4517eebaf..71497caee 100644 --- a/src/karts/emergency_animation.hpp +++ b/src/karts/emergency_animation.hpp @@ -75,8 +75,16 @@ public: void forceRescue(); void update(float dt); // ------------------------------------------------------------------------ - /** Returns true if an emergency animation is being played. - */ + /** Returns true if an emergency animation is being played. */ bool playingEmergencyAnimation() const {return m_kart_mode!=EA_NONE; } + + /** Returns if a rescue animation is being shown. */ + bool playingRescueAnimation() const {return m_kart_mode==EA_RESCUE; } + + /** Returns if an explosion animation is being shown. */ + bool playingExplosionAnimation() const {return m_kart_mode==EA_EXPLOSION; } + + /** Returns the timer for the currently played animation. */ + const float getAnimationTimer() const {return m_timer;} }; // EmergencyAnimation #endif diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 24071ec73..5e110f944 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -250,11 +250,10 @@ public: // ------------------------------------------------------------------------ /** Returns the current attachment. */ - Attachment *getAttachment() - { - return m_attachment; - } // getAttachment - + const Attachment* getAttachment() const {return m_attachment; } + // ------------------------------------------------------------------------ + /** Returns the current attachment, non-const version. */ + Attachment* getAttachment() {return m_attachment; } // ------------------------------------------------------------------------ /** Returns the camera of this kart (or NULL if no camera is attached * to this kart). */ diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index b55961296..f214554e1 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -31,6 +31,9 @@ #include "utils/translation.hpp" //----------------------------------------------------------------------------- +/** Constructs the linear world. Note that here no functions can be called + * that use World::getWorld(), since it is not yet defined. + */ LinearWorld::LinearWorld() : World() { m_kart_display_info = NULL; @@ -39,15 +42,20 @@ LinearWorld::LinearWorld() : World() } // LinearWorld // ---------------------------------------------------------------------------- +/** Actually initialises the world, i.e. creates all data structures to + * for all karts etc. In init functions can be called that use + * World::getWorld(). + */ void LinearWorld::init() { World::init(); const unsigned int kart_amount = m_karts.size(); - + m_position_index.resize(kart_amount); m_kart_display_info = new RaceGUIBase::KartIconDisplayInfo[kart_amount]; for(unsigned int n=0; nsetPosition(p); + m_position_index[p-1] = i; // Switch on faster music if not already done so, if the // first kart is doing its last lap, and if the estimated // remaining time is less than 30 seconds. diff --git a/src/modes/linear_world.hpp b/src/modes/linear_world.hpp index 2e9d9a653..c2be872f6 100644 --- a/src/modes/linear_world.hpp +++ b/src/modes/linear_world.hpp @@ -61,6 +61,9 @@ private: }; + /** This contains a mapping from race position to kart index. */ + std::vector m_position_index; + protected: RaceGUIBase::KartIconDisplayInfo* m_kart_display_info; @@ -98,7 +101,9 @@ public: virtual RaceGUIBase::KartIconDisplayInfo* getKartsDisplayInfo(); virtual void moveKartAfterRescue(Kart* kart); - + /** Returns the kart with position p, 1<=p<=num_karts). */ + const Kart* getKartAtPosition(unsigned int p) const + { return m_karts[m_position_index[p-1]]; } virtual void restartRace(); virtual bool raceHasLaps(){ return true; } diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index c7b37cfb4..78ca46af8 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -34,7 +34,11 @@ using namespace irr; #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/linear_world.hpp" #include "modes/world.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" @@ -92,6 +96,9 @@ RaceGUI::RaceGUI() m_string_ready = _("Ready!"); m_string_set = _("Set!"); m_string_go = _("Go!"); + + m_dist_show_overlap=2; + m_icons_inertia=2; } // RaceGUI //----------------------------------------------------------------------------- @@ -113,13 +120,15 @@ void RaceGUI::createMarkerTexture() while(npower2>1)-1; - IrrDriver::RTTProvider rttProvider(core::dimension2du(m_marker_rendered_size * npower2, - m_marker_rendered_size), + IrrDriver::RTTProvider rttProvider(core::dimension2du(m_marker_rendered_size + *npower2, + m_marker_rendered_size), "RaceGUI::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); + (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); @@ -144,11 +153,12 @@ void RaceGUI::createMarkerTexture() const std::string& kart_ident = race_manager->getKartIdent(i); assert(kart_ident.size() > 0); - const KartProperties *kp = kart_properties_manager->getKart(kart_ident); + 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) ); + 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) @@ -172,8 +182,10 @@ void RaceGUI::createMarkerTexture() 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); + 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 @@ -193,13 +205,15 @@ void RaceGUI::createMarkerTexture() void RaceGUI::createRegularPolygon(unsigned int n, float radius, const core::vector2df ¢er, const video::SColor &color, - video::S3DVertex *v, unsigned short int *index) + video::S3DVertex *v, + unsigned short int *index) { float f = 2*M_PI/(float)n; for (unsigned int i=0; igetNumLocalPlayers() == 3 && !GUIEngine::ModalDialog::isADialogActive()) + // Special case : when 3 players play, use 4th window to display such + // stuff (but we must clear it) + if (race_manager->getNumLocalPlayers() == 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)); + 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(); @@ -268,19 +287,25 @@ void RaceGUI::renderPlayerView(const Kart *kart) { 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; + 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()); + 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 */); + irr_driver->getVideoDriver()->draw2DImage(t, dest, source, + NULL /* clip */, + NULL /* color */, + true /* alpha */); } @@ -349,8 +374,10 @@ void RaceGUI::drawGlobalMiniMap() 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); + 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; @@ -358,7 +385,8 @@ void RaceGUI::drawGlobalMiniMap() 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); + irr_driver->getVideoDriver()->draw2DImage(m_marker, position, source, + NULL, NULL, true); } // for igetNumLocalPlayers() == 3) { - x = UserConfigParams::m_width/2 + 5; + x_base = UserConfigParams::m_width/2 + 15; y_base = UserConfigParams::m_height/2 + 20; } - + int x; int y; + float previous_distance=10000.0;//far ahead + + int ICON_WIDTH=(int)(40*(UserConfigParams::m_width/800.0f)); int ICON_PLAYER_WIDTH=(int)(50*(UserConfigParams::m_width/800.0f)); if(UserConfigParams::m_height<600) @@ -386,51 +417,179 @@ void RaceGUI::drawGlobalPlayerIcons(const KartIconDisplayInfo* info) ICON_PLAYER_WIDTH = 35; } + int previous_x=x_base; + int previous_y=y_base-ICON_PLAYER_WIDTH-2; + gui::ScalableFont* font = GUIEngine::getFont(); - World *world = World::getWorld(); + LinearWorld *world = (LinearWorld*)(World::getWorld()); const unsigned int kart_amount = world->getNumKarts(); - for(unsigned int i = 0; i < kart_amount ; i++) + + for(int position = 1; position <= (int)kart_amount ; position++) { - Kart* kart = world->getKart(i); + const Kart *kart = world->getKartAtPosition(position); if(kart->isEliminated()) continue; - const int position = kart->getPosition(); + unsigned int kart_id = kart->getWorldKartId(); - y = y_base + ( (position == -1 ? i : position-1)*(ICON_PLAYER_WIDTH+2)); - - if (info[i].m_text.size() > 0) + //x,y is the target position + int lap = info[kart->getWorldKartId()].lap; + float distance= + world->getDistanceDownTrackForKart(kart_id) + +world->getTrack()->getTrackLength()*lap; + if (previous_distance-distance pos(x+ICON_PLAYER_WIDTH, y+5, x+ICON_PLAYER_WIDTH, y+5); - core::stringw s=info[i].m_text.c_str(); + //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_x=x;//save coord of the previous kart in list + previous_y=y; + previous_distance=distance; + + //initialize m_previous_icons_position + if (m_previous_icons_position.size() pos(x,y); + m_previous_icons_position.push_back(pos); + } + + //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 (info[kart_id].m_text.size() > 0) + { + video::SColor color = video::SColor(255, + (int)(255*info[kart_id].r), + (int)(255*info[kart_id].g), + (int)(255*info[kart_id].b) ); + core::rect pos(x+ICON_PLAYER_WIDTH, y+5, + x+ICON_PLAYER_WIDTH, y+5); + core::stringw s=info[kart_id].m_text.c_str(); font->draw(s.c_str(), pos, color); } - - if (info[i].special_title.size() > 0) + + if (info[kart_id].special_title.size() > 0) { static video::SColor color = video::SColor(255, 255, 0, 0); - core::rect pos(x+ICON_PLAYER_WIDTH, y+5, x+ICON_PLAYER_WIDTH, y+5); - core::stringw s(info[i].special_title.c_str()); + core::rect pos(x+ICON_PLAYER_WIDTH, y+5, + x+ICON_PLAYER_WIDTH, y+5); + core::stringw s(info[kart_id].special_title.c_str()); font->draw(s.c_str(), pos, color); } // draw icon video::ITexture *icon = kart->getKartProperties()->getIconMaterial()->getTexture(); - int w = kart->getController()->isPlayerController() ? ICON_PLAYER_WIDTH - : ICON_WIDTH; + int w = + kart->getController()->isPlayerController() ? ICON_PLAYER_WIDTH + : ICON_WIDTH; const core::rect pos(x, y, x+w, y+w); // Fixes crash bug, why are certain icons not showing up? - if (icon != NULL) + if ((icon != NULL) && (!kart->playingEmergencyAnimation())) { - const core::rect rect(core::position2d(0,0), icon->getOriginalSize()); - irr_driver->getVideoDriver()->draw2DImage(icon, pos, rect, NULL, NULL, true); + const core::rect rect(core::position2d(0,0), + icon->getOriginalSize()); + irr_driver->getVideoDriver()->draw2DImage(icon, pos, rect, + NULL, NULL, true); } - } // next kart - + //draw status info + + if ((icon != NULL) && (kart->playingRescueAnimation())) + { + //icon fades to the left + float t_anim=100*sin(0.5f*M_PI*kart->getAnimationTimer()); + const core::rect rect1(core::position2d(0,0), + icon->getOriginalSize()); + const core::rect pos1((int)(x-t_anim), y, + (int)(x+w-t_anim), y+w); + irr_driver->getVideoDriver()->draw2DImage(icon, pos1, rect1, + NULL, NULL, true); + } + + if ((icon != NULL) && (kart->playingExplosionAnimation())) + { + //exploses into 4 parts + float t_anim=50.0f*sin(0.5f*M_PI*kart->getAnimationTimer()); + u16 icon_size_x=icon->getOriginalSize().Width; + u16 icon_size_y=icon->getOriginalSize().Height; + + const core::rect rect1(0, 0, icon_size_x/2,icon_size_y/2); + const core::rect pos1((int)(x-t_anim), (int)(y-t_anim), + (int)(x+w/2-t_anim), + (int)(y+w/2-t_anim)); + irr_driver->getVideoDriver()->draw2DImage(icon, pos1, rect1, + NULL, NULL, true); + + const core::rect rect2(icon_size_x/2,0, + icon_size_x,icon_size_y/2); + const core::rect pos2((int)(x+w/2+t_anim), + (int)(y-t_anim), + (int)(x+w+t_anim), + (int)(y+w/2-t_anim)); + irr_driver->getVideoDriver()->draw2DImage(icon, pos2, rect2, + NULL, NULL, true); + + const core::rect rect3(0, icon_size_y/2, icon_size_x/2,icon_size_y); + const core::rect pos3((int)(x-t_anim), (int)(y+w/2+t_anim), + (int)(x+w/2-t_anim), (int)(y+w+t_anim)); + irr_driver->getVideoDriver()->draw2DImage(icon, pos3, rect3, NULL, NULL, true); + + const core::rect rect4(icon_size_x/2,icon_size_y/2,icon_size_x,icon_size_y); + const core::rect pos4((int)(x+w/2+t_anim), (int)(y+w/2+t_anim), + (int)(x+w+t_anim), (int)(y+w+t_anim)); + irr_driver->getVideoDriver()->draw2DImage(icon, pos4, rect4, NULL, NULL, true); + } + + //Plunger + if (kart->hasViewBlockedByPlunger()) + { + video::ITexture *icon_plunger = + powerup_manager->getIcon(PowerupManager::POWERUP_PLUNGER)->getTexture(); + if (icon_plunger != NULL) + { + const core::rect rect(core::position2d(0,0), + icon_plunger->getOriginalSize()); + const core::rect pos1(x+10, y-10, x+w+10, y+w-10); + irr_driver->getVideoDriver()->draw2DImage(icon_plunger, pos1, + rect, NULL, NULL, + true); + } + } + //attachment + if (kart->getAttachment()->getType() != ATTACH_NOTHING) + { + video::ITexture *icon_attachment = + attachment_manager->getIcon(kart->getAttachment()->getType()) + ->getTexture(); + if (icon_attachment != NULL) + { + const core::rect rect(core::position2d(0,0), + icon_attachment->getOriginalSize()); + const core::rect pos1(x-20, y-10, x+w-20, y+w-10); + irr_driver->getVideoDriver()->draw2DImage(icon_attachment, + pos1, rect, NULL, + NULL, true); + } + } + + } //next position } // drawGlobalPlayerIcons //----------------------------------------------------------------------------- @@ -449,7 +608,8 @@ void RaceGUI::drawPowerupIcons(const Kart* kart, int itemSpacing = (int)(std::min(scaling.X, scaling.Y)*30); - int x1 = viewport.UpperLeftCorner.X + viewport.getWidth()/2 - (n * itemSpacing)/2; + int x1 = viewport.UpperLeftCorner.X + viewport.getWidth()/2 + - (n * itemSpacing)/2; int y1 = viewport.UpperLeftCorner.Y + (int)(20 * scaling.Y); assert(powerup != NULL); @@ -656,23 +816,30 @@ void RaceGUI::drawLap(const KartIconDisplayInfo* info, const Kart* kart, if (m_minimap_on_left) { // check if mini-map is within Y coords of this player. - // if the mini-map is not even in the viewport of this player, don't bother placing - // the lap text at the right of the minimap. - if (UserConfigParams::m_height - m_map_bottom - m_map_height > viewport.LowerRightCorner.Y) + // if the mini-map is not even in the viewport of this player, don't + // bother placing the lap text at the right of the minimap. + if (UserConfigParams::m_height - m_map_bottom - m_map_height + > viewport.LowerRightCorner.Y) { - pos.UpperLeftCorner.X = viewport.UpperLeftCorner.X + (int)(0.1f*UserConfigParams::m_width); + pos.UpperLeftCorner.X = viewport.UpperLeftCorner.X + + (int)(0.1f*UserConfigParams::m_width); } else { // place lap text at the right of the mini-map - const int calculated_x = viewport.UpperLeftCorner.X + (int)(0.05f*UserConfigParams::m_width); - pos.UpperLeftCorner.X = std::max(calculated_x, m_map_right_side_x + 15); // don't overlap minimap + const int calculated_x = viewport.UpperLeftCorner.X + + (int)(0.05f*UserConfigParams::m_width); + // don't overlap minimap + pos.UpperLeftCorner.X = std::max(calculated_x, + m_map_right_side_x + 15); } } else { - // mini-map is on the right, and lap text on right, so no overlap possible - pos.UpperLeftCorner.X = viewport.UpperLeftCorner.X + (int)(0.05f*UserConfigParams::m_width); + // mini-map is on the right, and lap text on right, + // so no overlap possible + pos.UpperLeftCorner.X = viewport.UpperLeftCorner.X + + (int)(0.05f*UserConfigParams::m_width); } gui::ScalableFont* font = GUIEngine::getFont(); @@ -735,7 +902,8 @@ void RaceGUI::drawAllMessages(const Kart* kart, const int w = (viewport.LowerRightCorner.X + viewport.UpperLeftCorner.X)/2; // draw less important first, at the very top of the screen - for (AllMessageType::const_iterator i = m_messages.begin(); i != m_messages.end(); ++i) + for (AllMessageType::const_iterator i = m_messages.begin(); + i != m_messages.end(); ++i) { TimedMessage const &msg = *i; if (!msg.m_important) @@ -744,8 +912,9 @@ void RaceGUI::drawAllMessages(const Kart* 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 */); + 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; } } @@ -756,18 +925,21 @@ void RaceGUI::drawAllMessages(const Kart* kart, // 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) + for (AllMessageType::const_iterator i = m_messages.begin(); + i != m_messages.end(); ++i) { TimedMessage const &msg = *i; - if (!msg.m_important) continue; // less important messages were already displayed + // 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 + m_max_font_height); GUIEngine::getFont()->draw(core::stringw(msg.m_message.c_str()).c_str(), - pos, msg.m_color, true /* hcenter */, true /* vcenter */); + pos, msg.m_color, true /* hcenter */, + true /* vcenter */); y += m_max_font_height; } // for i in all messages } // drawAllMessages @@ -777,10 +949,12 @@ void RaceGUI::drawAllMessages(const Kart* kart, * certain amount of time (unless time<0, then the message is displayed * once). **/ -void RaceGUI::addMessage(const core::stringw &msg, const Kart *kart, float time, - int font_size, const video::SColor &color, bool important) +void RaceGUI::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)); + m_messages.push_back(TimedMessage(msg, kart, time, font_size, color, + important)); } // addMessage //----------------------------------------------------------------------------- @@ -788,7 +962,8 @@ void RaceGUI::addMessage(const core::stringw &msg, const Kart *kart, float time, // usually the title and composer. void RaceGUI::drawGlobalMusicDescription() { - if (!UserConfigParams::m_music) return; // show no music description when it's off + // show no music description when it's off + if (!UserConfigParams::m_music) return; gui::IGUIFont* font = GUIEngine::getFont(); @@ -813,7 +988,7 @@ void RaceGUI::drawGlobalMusicDescription() const float resize3 = resize*resize*resize; - // ---- Get song name, and calculate its size, allowing us to position stuff + // Get song name, and calculate its size, allowing us to position stuff const MusicInformation* mi = music_manager->getCurrentMusic(); if (!mi) return; @@ -838,14 +1013,18 @@ void RaceGUI::drawGlobalMusicDescription() 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 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; + 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)); + 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()!="") @@ -853,27 +1032,31 @@ void RaceGUI::drawGlobalMusicDescription() 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); + 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 */); + 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()); + 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); - - + irr_driver->getVideoDriver()->draw2DImage(t, dest, source, + NULL, NULL, true); } // drawGlobalMusicDescription // ---------------------------------------------------------------------------- @@ -885,20 +1068,22 @@ void RaceGUI::drawGlobalReadySetGo() { case WorldStatus::READY_PHASE: { - //static video::SColor color = video::SColor(255, 230, 168, 158); 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); + 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, 230, 230, 158); 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); + 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); @@ -906,10 +1091,11 @@ void RaceGUI::drawGlobalReadySetGo() break; case WorldStatus::GO_PHASE: { - //static video::SColor color = video::SColor(255, 100, 209, 100); 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); + 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 diff --git a/src/states_screens/race_gui.hpp b/src/states_screens/race_gui.hpp index ef3173b9b..83c00297f 100644 --- a/src/states_screens/race_gui.hpp +++ b/src/states_screens/race_gui.hpp @@ -139,6 +139,12 @@ private: int m_max_font_height; int m_small_font_max_height; + /** Distance on track to begin showing overlap in drawGlobalPlayerIcons */ + float m_dist_show_overlap;///can be zero + float m_icons_inertia;///can be zero + /** previous position of icons */ + std::vector< core::vector2d > m_previous_icons_position; + void createMarkerTexture(); void createRegularPolygon(unsigned int n, float radius, const core::vector2df ¢er,