From 5ab305771c720b75bd3488d4c295355a67c33d89 Mon Sep 17 00:00:00 2001 From: magned Date: Mon, 30 Jan 2012 07:35:04 +0000 Subject: [PATCH 01/47] Added particles for 2.5 sec skidding and 3.5+ sec skidding. Feel free to improve. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10764 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/gfx/skid1.xml | 4 ++-- data/gfx/skid2.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/gfx/skid1.xml b/data/gfx/skid1.xml index 8af886aba..cbbf13b7d 100644 --- a/data/gfx/skid1.xml +++ b/data/gfx/skid1.xml @@ -7,12 +7,12 @@ y="0.001" z="-0.003" /> - + - diff --git a/data/gfx/skid2.xml b/data/gfx/skid2.xml index 400b8b24a..0ca23fd50 100644 --- a/data/gfx/skid2.xml +++ b/data/gfx/skid2.xml @@ -7,7 +7,7 @@ y="0.001" z="-0.003" /> - + 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

0#6zsByX+P|M`=>3WIkM<`8At(Ph{ak&M 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 literal 0 HcmV?d00001 diff --git a/data/models/trophy_gold.b3d b/data/models/trophy_gold.b3d new file mode 100644 index 0000000000000000000000000000000000000000..3ff39676b91012a0530cac5cd5e5bb34b65bbb3b GIT binary patch literal 85841 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 Date: Wed, 8 Feb 2012 20:22:00 +0000 Subject: [PATCH 33/47] Improved collision handling to better detect left/right side collisions. Made collisions more bouncy than before (but I am still tuning). git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10827 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/stk_config.xml | 2 +- .../controller/default_ai_controller.cpp | 1 + src/physics/btKart.cpp | 54 ++++--- src/physics/btKart.hpp | 2 +- src/physics/physics.cpp | 143 ++++++++---------- 5 files changed, 97 insertions(+), 105 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index d5e0d6184..fe823f9c2 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -312,7 +312,7 @@ bevelling, and uses a simple box shape. As an example, a value of 1 for x and z will result in a sharp 'arrow' like shape. --> - was hit on left side, gets + // push to right), but that can lead to both karts being pushed in the + // same direction (front left of kart hits rear left). + // So we just use a simple test (which does the right thing in ideal + // crashes, but avoids pushing both karts in corner cases + // - pun intended ;) ). + if(contact_point_a.getX() < contact_point_b.getX()) { - faster_kart = kart_a; - faster_cp = contact_point_a; - slower_kart = kart_b; - slower_cp = contact_point_b; + push_right = kart_a; + push_left = kart_b; } else { - faster_kart = kart_b; - faster_cp = contact_point_b; - slower_kart = kart_a; - slower_cp = contact_point_a; + push_right = kart_b; + push_left = kart_a; } - CollisionSide faster_side = getCollisionSide(faster_kart->getBody(), - faster_cp); - CollisionSide slower_side = getCollisionSide(slower_kart->getBody(), - slower_cp); + // Add a scaling factor depending on the mass (avoid div by zero) + float f_left = push_left->getKartProperties()->getMass() > 0 + ? push_right->getKartProperties()->getMass() + / push_left->getKartProperties()->getMass() + : 1.5f; + // Add a scaling factor depending on speed (avoid div by 0) + f_left *= push_left->getSpeed() > 0 + ? push_right->getSpeed() + / push_left->getSpeed() + : 1.5f; + // Cap left to [0.8,1.25], which results in f_right being + // capped in the same interval + if(f_left > 1.25f) + f_left = 1.25f; + else if(f_left < 0.8f) + f_left = 0.8f; - // This probably needs adjusting once we have different kart properties. - // E.g. besides speed we might also want to take mass into account(?) - if(faster_side==COL_FRONT) + float f_right = f_left ==0 ? 1.5f : 1/f_left; + f_left = f_left * f_left * f_left; + f_right = f_right * f_right * f_right; + + // First push one kart to the left (if there is not already + // an impulse happening - one collision might cause more + // than one impulse otherwise) + if(push_left->getVehicle()->getCentralImpulseTime()<=0) { - // Special case: the faster kart hits a kart front on. In this case - // the slower kart will be pushed out of the faster kart's way - Vec3 dir = faster_kart->getVelocity(); - - // The direction in which the impulse will be applied depends on - // which side of the faster kart was hitting it: if the hit is - // on the right side of the faster kart, it will push the slower - // kart to the right and vice versa. This is based on the - // assumption that a hit to the right indicates that it's - // shorter to push the slower kart to the right. - Vec3 impulse; - if(faster_cp.getX()>0) - impulse = Vec3( dir.getZ(), 0, -dir.getX()); - else - impulse = Vec3(-dir.getZ(), 0, dir.getX()); - impulse.normalize(); - impulse *= faster_kart->getKartProperties()->getCollisionImpulse(); - float t = - faster_kart->getKartProperties()->getCollisionImpulseTime(); - if(t>0) - slower_kart->getVehicle()->setTimedCentralImpulse(t, impulse); - else - slower_kart->getBody()->applyCentralImpulse(impulse); - slower_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); - // Apply some impulse to the slower kart as well? + const KartProperties *kp = push_right->getKartProperties(); + Vec3 impulse(-kp->getCollisionImpulse()*f_left, 0, 0); + impulse = push_left->getTrans().getBasis() * impulse; + push_left->getVehicle() + ->setTimedCentralImpulse(kp->getCollisionImpulseTime(), + impulse); + push_left ->getBody()->setAngularVelocity(btVector3(0,0,0)); } - else + + // Then push the other kart to the right (if there is no + // impulse happening atm). + if(push_right->getVehicle()->getCentralImpulseTime()<=0) { - // Non-frontal collision, push the two karts away from each other - // First the faster kart - Vec3 dir = faster_kart->getVelocity(); - Vec3 impulse; - if(faster_cp.getX()>0) - impulse = Vec3(-dir.getZ(), 0, dir.getX()); - else - impulse = Vec3( dir.getZ(), 0, -dir.getX()); - impulse.normalize(); - impulse *= slower_kart->getKartProperties()->getCollisionImpulse(); - float t = - faster_kart->getKartProperties()->getCollisionImpulseTime(); - if(t>0) - faster_kart->getVehicle()->setTimedCentralImpulse(t, impulse); - else - faster_kart->getBody()->applyCentralImpulse(impulse); - faster_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); - - // Then the slower kart - dir = slower_kart->getVelocity(); - if(slower_cp.getX()>0) - impulse = Vec3(-dir.getZ(), 0, dir.getX()); - else - impulse = Vec3( dir.getZ(), 0, -dir.getX()); - - impulse.normalize(); - impulse *= faster_kart->getKartProperties()->getCollisionImpulse(); - t = faster_kart->getKartProperties()->getCollisionImpulseTime(); - if(t>0) - slower_kart->getVehicle()->setTimedCentralImpulse(t, impulse); - else - slower_kart->getBody()->applyCentralImpulse(impulse); - slower_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); + const KartProperties *kp = push_left->getKartProperties(); + Vec3 impulse = Vec3(kp->getCollisionImpulse()*f_right, 0, 0); + impulse = push_right->getTrans().getBasis() * impulse; + push_right->getVehicle() + ->setTimedCentralImpulse(kp->getCollisionImpulseTime(), + impulse); + push_right->getBody()->setAngularVelocity(btVector3(0,0,0)); } } // KartKartCollision From 8cfe69333f482fbaea3e6428a6190ebb675ac139 Mon Sep 17 00:00:00 2001 From: auria Date: Thu, 9 Feb 2012 03:08:32 +0000 Subject: [PATCH 35/47] Port track info dialog to XML git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10829 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/gui/track_info_dialog.stkgui | 60 +++++++ src/guiengine/engine.cpp | 3 + src/guiengine/layout_manager.cpp | 6 +- src/guiengine/widgets/spinner_widget.hpp | 2 + .../dialogs/track_info_dialog.cpp | 153 +++++------------- .../dialogs/track_info_dialog.hpp | 13 +- 6 files changed, 120 insertions(+), 117 deletions(-) create mode 100644 data/gui/track_info_dialog.stkgui diff --git a/data/gui/track_info_dialog.stkgui b/data/gui/track_info_dialog.stkgui new file mode 100644 index 000000000..7add6e0ce --- /dev/null +++ b/data/gui/track_info_dialog.stkgui @@ -0,0 +1,60 @@ + + +

+ +
+ diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index ef394109c..f4e574eb3 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -372,6 +372,9 @@ namespace GUIEngine Another possible value is "fit", which will make a \ fit to its contents. + Another possible value is "font", which will use the size of the font + (useful to insert widgets inside text) + \n \subsection prop9 PROP_MAX_WIDTH, PROP_MAX_HEIGHT Names in XML files: \c "max_width", \c "max_height" diff --git a/src/guiengine/layout_manager.cpp b/src/guiengine/layout_manager.cpp index 83b872d63..259a9be7d 100644 --- a/src/guiengine/layout_manager.cpp +++ b/src/guiengine/layout_manager.cpp @@ -259,7 +259,8 @@ void LayoutManager::readCoords(Widget* self) // width { int abs_w = -1, percent_w = -1; - if (convertToCoord(width, &abs_w, &percent_w )) + if (width == "font") self->m_absolute_w = GUIEngine::getFontHeight(); + else if (convertToCoord(width, &abs_w, &percent_w )) { if (abs_w > -1) self->m_absolute_w = abs_w; else if (percent_w > -1) self->m_relative_w = (float)percent_w; @@ -271,7 +272,8 @@ void LayoutManager::readCoords(Widget* self) // height { int abs_h = -1, percent_h = -1; - if (convertToCoord(height, &abs_h, &percent_h )) + if (height == "font") self->m_absolute_h = GUIEngine::getFontHeight(); + else if (convertToCoord(height, &abs_h, &percent_h )) { if (abs_h > -1) self->m_absolute_h = abs_h; else if (percent_h > -1) self->m_relative_h = (float)percent_h; diff --git a/src/guiengine/widgets/spinner_widget.hpp b/src/guiengine/widgets/spinner_widget.hpp index a6f087288..71cffe6ee 100644 --- a/src/guiengine/widgets/spinner_widget.hpp +++ b/src/guiengine/widgets/spinner_widget.hpp @@ -153,6 +153,8 @@ namespace GUIEngine */ int getMin() const { return m_min; } + void setMin(int n) { m_min = n; } + /** Override method from base class Widget */ virtual void setActivated(); diff --git a/src/states_screens/dialogs/track_info_dialog.cpp b/src/states_screens/dialogs/track_info_dialog.cpp index 45725dc27..a423db78d 100644 --- a/src/states_screens/dialogs/track_info_dialog.cpp +++ b/src/states_screens/dialogs/track_info_dialog.cpp @@ -20,6 +20,8 @@ #include "guiengine/screen.hpp" #include "guiengine/widgets/button_widget.hpp" #include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/spinner_widget.hpp" #include "io/file_manager.hpp" #include "karts/kart_properties_manager.hpp" #include "network/network_manager.hpp" @@ -49,52 +51,32 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin const irr::core::stringw& trackName, ITexture* screenshot, const float w, const float h) : ModalDialog(w, h) { + loadFromFile("track_info_dialog.stkgui"); + const bool has_laps = race_manager->modeHasLaps(); const bool has_highscores = race_manager->modeHasHighscores(); - const int y1 = m_area.getHeight()/7; - const int y2 = m_area.getHeight()*5/7; - const int y3 = m_area.getHeight()*6/7; - m_track_ident = trackIdent; m_ribbon_item = ribbonItem; - // ---- Track title - core::rect< s32 > area_top(0, 0, m_area.getWidth(), y1); - IGUIStaticText* a = GUIEngine::getGUIEnv()->addStaticText( trackName.c_str(), - area_top, false, true, // border, word warp - m_irrlicht_window); - a->setTabStop(false); - - const int hscores_y_from = y1; - const int hscores_y_to = y1 + (y2 - y1)*2/3; + getWidget("name")->setText(trackName.c_str(), false); - // ---- Track credits Track* track = track_manager->getTrack(trackIdent); - - core::rect< s32 > creator_info_area(0, hscores_y_to, m_area.getWidth()/2, y2); - //I18N: when showing who is the author of track '%s' (place %s where the name of the author should appear) - stringw text = _("Track by %s", track->getDesigner().c_str()); + getWidget("author")->setText( _("Track by %s", track->getDesigner().c_str()), false ); - IGUIStaticText* b = GUIEngine::getGUIEnv()->addStaticText( text.c_str(), - creator_info_area, false , true , // border, word warp - m_irrlicht_window); - b->setTabStop(false); - - // ---- Track screenshot + Widget* screenshot_div = getWidget("screenshot_div"); IconButtonWidget* screenshotWidget = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO, false /* tab stop */, false /* focusable */); // images are saved squared, but must be stretched to 4: screenshotWidget->setCustomAspectRatio(4.0f / 3.0f); - core::rect< s32 > area_right(m_area.getWidth()/2, y1, m_area.getWidth(), y2-10); - screenshotWidget->m_x = area_right.UpperLeftCorner.X; - screenshotWidget->m_y = area_right.UpperLeftCorner.Y; - screenshotWidget->m_w = area_right.getWidth(); - screenshotWidget->m_h = area_right.getHeight(); + screenshotWidget->m_x = screenshot_div->m_x; + screenshotWidget->m_y = screenshot_div->m_y; + screenshotWidget->m_w = screenshot_div->m_w; + screenshotWidget->m_h = screenshot_div->m_h; // temporary icon, will replace it just after (but it will be shown if the given icon is not found) screenshotWidget->m_properties[PROP_ICON] = "gui/main_help.png"; @@ -107,70 +89,60 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin } m_widgets.push_back(screenshotWidget); - a->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); - b->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); - + // ---- Lap count m_spinner if (has_laps) { - m_spinner = new SpinnerWidget(); - m_spinner->m_x = m_area.getWidth()/2 - 200; - m_spinner->m_y = y2; - m_spinner->m_w = 400; - m_spinner->m_h = y3 - y2 - 15; - m_spinner->setParent(m_irrlicht_window); + m_spinner = getWidget("lapcountspinner"); m_spinner->m_properties[PROP_ID] = "lapcountspinner"; if (UserConfigParams::m_artist_debug_mode) { - m_spinner->m_properties[PROP_MIN_VALUE] = "0"; + m_spinner->setMin(0); } - else - { - m_spinner->m_properties[PROP_MIN_VALUE] = "1"; - } - m_spinner->m_properties[PROP_MAX_VALUE] = "99"; - m_spinner->m_properties[PROP_WARP_AROUND] = "true"; //I18N: In the track setup screen (number of laps choice, where %i is the number) - m_spinner->setText( _("%i laps") ); - - m_widgets.push_back(m_spinner); - m_spinner->add(); + //m_spinner->setText( _("%i laps") ); m_spinner->setValue( UserConfigParams::m_num_laps ); - m_spinner->getIrrlichtElement()->setTabStop(true); - m_spinner->getIrrlichtElement()->setTabGroup(false); + //m_spinner->getIrrlichtElement()->setTabStop(true); + //m_spinner->getIrrlichtElement()->setTabGroup(false); + + const int num_laps = m_spinner->getValue(); + race_manager->setNumLaps(num_laps); } else { m_spinner = NULL; } - - // ---- Start button - ButtonWidget* okBtn = new ButtonWidget(); - okBtn->m_properties[PROP_ID] = "start"; - okBtn->setText( _("Start Race") ); - okBtn->m_x = m_area.getWidth()/2 - 200; - okBtn->m_y = y3; - okBtn->m_w = 400; - okBtn->m_h = m_area.getHeight() - y3 - 15; - okBtn->setParent(m_irrlicht_window); - m_widgets.push_back(okBtn); - okBtn->add(); - okBtn->getIrrlichtElement()->setTabStop(true); - okBtn->getIrrlichtElement()->setTabGroup(false); + // ---- High Scores if (has_highscores) { - addHighScoreWidgets(hscores_y_from, hscores_y_to); - const int num_laps = m_spinner->getValue(); - race_manager->setNumLaps(num_laps); + m_kart_icons[0] = getWidget("iconscore1"); + m_kart_icons[1] = getWidget("iconscore2"); + m_kart_icons[2] = getWidget("iconscore3"); + + m_highscore_entries[0] = getWidget("highscore1"); + m_highscore_entries[1] = getWidget("highscore2"); + m_highscore_entries[2] = getWidget("highscore3"); + updateHighScores(); + + } + else + { + getWidget("iconscore1")->setVisible(false); + getWidget("iconscore2")->setVisible(false); + getWidget("iconscore3")->setVisible(false); + + getWidget("highscores")->setVisible(false); + getWidget("highscore1")->setVisible(false); + getWidget("highscore2")->setVisible(false); + getWidget("highscore3")->setVisible(false); } - - okBtn->setFocusForPlayer( PLAYER_ID_GAME_MASTER ); + getWidget("start")->setFocusForPlayer( PLAYER_ID_GAME_MASTER ); } @@ -190,44 +162,6 @@ TrackInfoDialog::~TrackInfoDialog() // ------------------------------------------------------------------------------------------------------ -void TrackInfoDialog::addHighScoreWidgets(const int hscores_y_from, const int hscores_y_to) -{ - ITexture* texture = irr_driver->getTexture( (file_manager->getGUIDir() + "/random_kart.png").c_str() ) ; - - core::rect< s32 > hiscores_title_area(5, hscores_y_from, m_area.getWidth()/2, hscores_y_from + 30); - stringw text = _("= Highscores ="); - IGUIStaticText* hscores_header = GUIEngine::getGUIEnv()->addStaticText( text.c_str(), hiscores_title_area, - false , true , // border, word warp - m_irrlicht_window); - hscores_header->setTextRestrainedInside(false); - hscores_header->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); - - // fill highscore entries - for (int n=0; n icon_area(5, from_y + gap, 5 + icon_size, from_y + icon_size); - - m_kart_icons[n] = GUIEngine::getGUIEnv()->addImage( icon_area, m_irrlicht_window ); - m_kart_icons[n]->setImage(texture); - m_kart_icons[n]->setScaleImage(true); - m_kart_icons[n]->setTabStop(false); - m_kart_icons[n]->setUseAlphaChannel(true); - - core::rect< s32 > entry_area(icon_size + 10, from_y, m_area.getWidth()/2, next_from_y); - m_highscore_entries[n] = GUIEngine::getGUIEnv()->addStaticText( L"", entry_area, - false , true , // border, word warp - m_irrlicht_window); - } -} - -// ------------------------------------------------------------------------------------------------------ - void TrackInfoDialog::updateHighScores() { std::string game_mode_ident = RaceManager::getIdentOf( race_manager->getMinorMode() ); @@ -270,7 +204,6 @@ void TrackInfoDialog::updateHighScores() { //I18N: for empty highscores entries line = _("(Empty)"); - line += "\n"; ITexture* no_kart_texture = irr_driver->getTexture( (file_manager->getGUIDir() + "/random_kart.png").c_str() ) ; @@ -278,7 +211,7 @@ void TrackInfoDialog::updateHighScores() } - m_highscore_entries[n]->setText( line.c_str() ); + m_highscore_entries[n]->setText( line.c_str(), false ); } } diff --git a/src/states_screens/dialogs/track_info_dialog.hpp b/src/states_screens/dialogs/track_info_dialog.hpp index 6e699fccf..b3bf49e6a 100644 --- a/src/states_screens/dialogs/track_info_dialog.hpp +++ b/src/states_screens/dialogs/track_info_dialog.hpp @@ -20,11 +20,15 @@ #define HEADER_TRACKINFO_DIALOG_HPP #include "guiengine/modaldialog.hpp" -#include "guiengine/widgets/spinner_widget.hpp" static const int HIGHSCORE_COUNT = 3; -namespace irr { namespace gui { class IGUIImage; class IGUIStaticText; } } +namespace GUIEngine +{ + class SpinnerWidget; + class IconButtonWidget; + class LabelWidget; +} /** * \brief Dialog that shows the information about a given track @@ -38,10 +42,9 @@ class TrackInfoDialog : public GUIEngine::ModalDialog // When there is no need to tab through / click on images/labels, we can add directly // irrlicht labels (more complicated uses require the use of our widget set) GUIEngine::SpinnerWidget* m_spinner; - irr::gui::IGUIImage* m_kart_icons[HIGHSCORE_COUNT]; - irr::gui::IGUIStaticText* m_highscore_entries[HIGHSCORE_COUNT]; + GUIEngine::IconButtonWidget* m_kart_icons[HIGHSCORE_COUNT]; + GUIEngine::LabelWidget* m_highscore_entries[HIGHSCORE_COUNT]; - void addHighScoreWidgets(const int hscores_y_from, const int hscores_y_to); void updateHighScores(); public: From 553556446737ad84e2b17c1bf9eb997b59a4139a Mon Sep 17 00:00:00 2001 From: hikerstk Date: Thu, 9 Feb 2012 05:41:13 +0000 Subject: [PATCH 36/47] Consider the 'sideway' speed with which karts hit each other: this makes it possible to intentionally push karts off track by crashing into them sideways. This is experimental ;) Also renamed some variables to better describe what they are. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10830 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/physics/physics.cpp | 91 +++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 984487cc4..14eac4b0b 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -287,7 +287,7 @@ void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, kart_a->crashed(kart_b, /*handle_attachments*/true); kart_b->crashed(kart_a, /*handle_attachments*/false); - Kart *push_left, *push_right; + Kart *left_kart, *right_kart; // Determine which kart is pushed to the left, and which one to the // right. Ideally the sign of the X coordinate of the local conact point @@ -299,61 +299,92 @@ void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, // - pun intended ;) ). if(contact_point_a.getX() < contact_point_b.getX()) { - push_right = kart_a; - push_left = kart_b; + left_kart = kart_a; + right_kart = kart_b; } else { - push_right = kart_b; - push_left = kart_a; + left_kart = kart_b; + right_kart = kart_a; } // Add a scaling factor depending on the mass (avoid div by zero) - float f_left = push_left->getKartProperties()->getMass() > 0 - ? push_right->getKartProperties()->getMass() - / push_left->getKartProperties()->getMass() - : 1.5f; + float f_right = right_kart->getKartProperties()->getMass() > 0 + ? left_kart->getKartProperties()->getMass() + / right_kart->getKartProperties()->getMass() + : 1.5f; // Add a scaling factor depending on speed (avoid div by 0) - f_left *= push_left->getSpeed() > 0 - ? push_right->getSpeed() - / push_left->getSpeed() + f_right *= right_kart->getSpeed() > 0 + ? left_kart->getSpeed() + / right_kart->getSpeed() : 1.5f; - // Cap left to [0.8,1.25], which results in f_right being + // Cap f_right to [0.8,1.25], which results in f_left being // capped in the same interval - if(f_left > 1.25f) - f_left = 1.25f; - else if(f_left < 0.8f) - f_left = 0.8f; + if(f_right > 1.25f) + f_right = 1.25f; + else if(f_right< 0.8f) + f_right = 0.8f; + float f_left = f_right ==0 ? 1.5f : 1/f_right; - float f_right = f_left ==0 ? 1.5f : 1/f_left; - f_left = f_left * f_left * f_left; - f_right = f_right * f_right * f_right; + // Check if a kart is more 'actively' trying to push another kart + // by checking its local sidewards velocity + float vel_left = left_kart->getVelocityLC().getX(); + float vel_right = right_kart->getVelocityLC().getX(); + + // Use the difference in speed to determine which kart gets a + // ramming bonus. Normally vel_right and vel_left will have + // a different sign: right kart will be driving to the left, + // and left kart to the right (both pushing at each other). + // By using the sum we get the intended effect: if both karts + // are pushing with the same speed, vel_diff is 0, if the right + // kart is driving faster vel_diff will be < 0. If both velocities + // have the same sign, one kart is trying to steer away from the + // other, in which case it gets an even bigger push. + float vel_diff = vel_right + vel_left; + + // More driving towards left --> left kart gets bigger impulse + if(vel_diff<0) + { + f_right *= vel_left == 0 ? 2.0f : (1.0f - vel_diff/fabsf(vel_left)); + if(f_right > 2.0f) + f_right = 2.0f; + } + else + { + f_left *= vel_right == 0 ? 2.0f : (1.0f + vel_diff/fabsf(vel_right)); + if(f_left > 2.0f) + f_left = 2.0f; + } + + // Increase the effect somewhat by squaring the factors + f_left = f_left * f_left; + f_right = f_right * f_right; // First push one kart to the left (if there is not already // an impulse happening - one collision might cause more // than one impulse otherwise) - if(push_left->getVehicle()->getCentralImpulseTime()<=0) + if(right_kart->getVehicle()->getCentralImpulseTime()<=0) { - const KartProperties *kp = push_right->getKartProperties(); + const KartProperties *kp = left_kart->getKartProperties(); Vec3 impulse(-kp->getCollisionImpulse()*f_left, 0, 0); - impulse = push_left->getTrans().getBasis() * impulse; - push_left->getVehicle() + impulse = right_kart->getTrans().getBasis() * impulse; + right_kart->getVehicle() ->setTimedCentralImpulse(kp->getCollisionImpulseTime(), impulse); - push_left ->getBody()->setAngularVelocity(btVector3(0,0,0)); + right_kart ->getBody()->setAngularVelocity(btVector3(0,0,0)); } // Then push the other kart to the right (if there is no // impulse happening atm). - if(push_right->getVehicle()->getCentralImpulseTime()<=0) + if(left_kart->getVehicle()->getCentralImpulseTime()<=0) { - const KartProperties *kp = push_left->getKartProperties(); + const KartProperties *kp = right_kart->getKartProperties(); Vec3 impulse = Vec3(kp->getCollisionImpulse()*f_right, 0, 0); - impulse = push_right->getTrans().getBasis() * impulse; - push_right->getVehicle() + impulse = left_kart->getTrans().getBasis() * impulse; + left_kart->getVehicle() ->setTimedCentralImpulse(kp->getCollisionImpulseTime(), impulse); - push_right->getBody()->setAngularVelocity(btVector3(0,0,0)); + left_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); } } // KartKartCollision From e21f040919c8a7a9fd48e83e65569517d21d8af4 Mon Sep 17 00:00:00 2001 From: auria Date: Mon, 13 Feb 2012 01:13:18 +0000 Subject: [PATCH 40/47] When LOD is used incorrectly, give error message instead of crashing git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10838 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/graphics/lod_node.hpp | 6 +++++- src/tracks/track.cpp | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/graphics/lod_node.hpp b/src/graphics/lod_node.hpp index 676c31dc8..d0e95da09 100644 --- a/src/graphics/lod_node.hpp +++ b/src/graphics/lod_node.hpp @@ -92,7 +92,11 @@ public: void add(int level, scene::ISceneNode* node, bool reparent); /** Get the highest level of detail node */ - scene::ISceneNode* getFirstNode() { assert(m_nodes.size() > 0); return m_nodes[0]; } + scene::ISceneNode* getFirstNode() + { + if (m_nodes.size() > 0) return m_nodes[0]; + else return NULL; + } std::vector& getAllNodes() { return m_nodes; } diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 417f296e9..eca6fa21b 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -470,6 +470,11 @@ void Track::convertTrackToBullet(scene::ISceneNode *node) if (node->getType() == scene::ESNT_LOD_NODE) { node = ((LODNode*)node)->getFirstNode(); + if (node == NULL) + { + fprintf(stderr, "[Track] WARNING: this track contains an empty LOD group\n"); + return; + } } const core::vector3df &pos = node->getPosition(); From 5b6e31639ce7c988533a0b6e44d116299afca34e Mon Sep 17 00:00:00 2001 From: auria Date: Mon, 13 Feb 2012 15:35:42 +0000 Subject: [PATCH 41/47] Potential crash fix git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10839 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/graphics/lod_node.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/lod_node.cpp b/src/graphics/lod_node.cpp index fe7cdf309..d3030b4f9 100644 --- a/src/graphics/lod_node.cpp +++ b/src/graphics/lod_node.cpp @@ -63,6 +63,7 @@ void LODNode::render() void LODNode::OnRegisterSceneNode() { if (!isVisible()) return; + if (m_nodes.size() == 0) return; // TODO: optimize this, there is no need to check every frame scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera(); From 418aaf84e7f800240973823339fb7e10ce83d1f4 Mon Sep 17 00:00:00 2001 From: auria Date: Mon, 13 Feb 2012 23:23:10 +0000 Subject: [PATCH 42/47] Fix use of unintialized pointer and improve error reporting git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10840 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/track.cpp | 3 ++- src/tracks/track_object.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index eca6fa21b..8ec7ef29b 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -472,7 +472,8 @@ void Track::convertTrackToBullet(scene::ISceneNode *node) node = ((LODNode*)node)->getFirstNode(); if (node == NULL) { - fprintf(stderr, "[Track] WARNING: this track contains an empty LOD group\n"); + fprintf(stderr, "[Track] WARNING: this track contains an empty LOD group : '%s'\n", + ((LODNode*)node)->getGroupName().c_str()); return; } } diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index 1e3f929d6..2bbc83164 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -45,6 +45,7 @@ TrackObject::TrackObject(const XMLNode &xml_node) m_enabled = true; m_is_looped = false; m_sound = NULL; + m_mesh = NULL; xml_node.get("xyz", &m_init_xyz ); xml_node.get("hpr", &m_init_hpr ); @@ -136,6 +137,7 @@ TrackObject::TrackObject(const XMLNode &xml_node) { std::string full_path = World::getWorld()->getTrack()->getTrackFile(model_name); + if(file_manager->fileExists(full_path)) { m_mesh = irr_driver->getAnimatedMesh(full_path); @@ -146,11 +148,12 @@ TrackObject::TrackObject(const XMLNode &xml_node) // in STK's model directory. full_path = file_manager->getModelFile(model_name); m_mesh = irr_driver->getAnimatedMesh(full_path); + if(!m_mesh) { fprintf(stderr, - "Warning: '%s' in '%s' not found and is ignored.\n", - xml_node.getName().c_str(), model_name.c_str()); + "Warning: model '%s' in node '%s' not found and is ignored.\n", + model_name.c_str(), xml_node.getName().c_str()); return; } // if(!m_mesh) } From 43ba4c4c254a0073afd7ce22ac3f0717b31810de Mon Sep 17 00:00:00 2001 From: auria Date: Mon, 13 Feb 2012 23:32:15 +0000 Subject: [PATCH 43/47] Fix another unitialized variable git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10841 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/track_object.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index 2bbc83164..f8c49a6e6 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -46,6 +46,7 @@ TrackObject::TrackObject(const XMLNode &xml_node) m_is_looped = false; m_sound = NULL; m_mesh = NULL; + m_node = NULL; xml_node.get("xyz", &m_init_xyz ); xml_node.get("hpr", &m_init_hpr ); From c54a651ce9fbf397c39f27929bdf58b60c533206 Mon Sep 17 00:00:00 2001 From: auria Date: Mon, 13 Feb 2012 23:40:27 +0000 Subject: [PATCH 44/47] When failing to load a track object, don't leave a malformed TrackObject instance in the track_object_manager git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10842 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/track_object.cpp | 7 +-- src/tracks/track_object_manager.cpp | 80 ++++++++++++++++------------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index f8c49a6e6..daad21a58 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -152,11 +152,8 @@ TrackObject::TrackObject(const XMLNode &xml_node) if(!m_mesh) { - fprintf(stderr, - "Warning: model '%s' in node '%s' not found and is ignored.\n", - model_name.c_str(), xml_node.getName().c_str()); - return; - } // if(!m_mesh) + throw std::runtime_error("Model '" + model_name + "' cannot be found"); + } } m_mesh->grab(); diff --git a/src/tracks/track_object_manager.cpp b/src/tracks/track_object_manager.cpp index a47d1070a..b9f84d153 100644 --- a/src/tracks/track_object_manager.cpp +++ b/src/tracks/track_object_manager.cpp @@ -47,50 +47,58 @@ TrackObjectManager::~TrackObjectManager() */ void TrackObjectManager::add(const XMLNode &xml_node) { - std::string groupname; - xml_node.get("lod_group", &groupname); - bool is_lod = !groupname.empty(); - - std::string type; - xml_node.get("type", &type); - if(type=="movable") + try { - if (is_lod) + std::string groupname; + xml_node.get("lod_group", &groupname); + bool is_lod = !groupname.empty(); + + std::string type; + xml_node.get("type", &type); + if(type=="movable") { - m_lod_objects[groupname].push_back(new PhysicalObject(xml_node)); + if (is_lod) + { + m_lod_objects[groupname].push_back(new PhysicalObject(xml_node)); + } + else + { + m_all_objects.push_back(new PhysicalObject(xml_node)); + } + } + else if(type=="animation") + { + if (is_lod) + { + m_lod_objects[groupname].push_back(new ThreeDAnimation(xml_node)); + } + else + { + m_all_objects.push_back(new ThreeDAnimation(xml_node)); + } + } + else if(type=="billboard") + { + m_all_objects.push_back(new BillboardAnimation(xml_node)); + } + else if(type=="sfx-emitter") + { + m_all_objects.push_back(new TrackObject(xml_node)); + } + else if(type=="action-trigger") + { + m_all_objects.push_back(new TrackObject(xml_node)); } else { - m_all_objects.push_back(new PhysicalObject(xml_node)); + fprintf(stderr, "Unknown track object: '%s' - ignored.\n", + type.c_str()); } } - else if(type=="animation") + catch (std::exception& e) { - if (is_lod) - { - m_lod_objects[groupname].push_back(new ThreeDAnimation(xml_node)); - } - else - { - m_all_objects.push_back(new ThreeDAnimation(xml_node)); - } - } - else if(type=="billboard") - { - m_all_objects.push_back(new BillboardAnimation(xml_node)); - } - else if(type=="sfx-emitter") - { - m_all_objects.push_back(new TrackObject(xml_node)); - } - else if(type=="action-trigger") - { - m_all_objects.push_back(new TrackObject(xml_node)); - } - else - { - fprintf(stderr, "Unknown track object: '%s' - ignored.\n", - type.c_str()); + fprintf(stderr, "[TrackObjectManager] WARNING: Could not load track object. Reason : %s\n", + e.what()); } } // add From b6e898876a0c4e2ca34623ce4f8ea9d13a33b402 Mon Sep 17 00:00:00 2001 From: auria Date: Wed, 15 Feb 2012 01:24:17 +0000 Subject: [PATCH 45/47] Tweak a challenge git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10847 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/challenges/olivermath.challenge | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/challenges/olivermath.challenge b/data/challenges/olivermath.challenge index 33e4d289f..1bee991ad 100644 --- a/data/challenges/olivermath.challenge +++ b/data/challenges/olivermath.challenge @@ -9,11 +9,11 @@ - + - + From 2d926bbfcdf6f68ba2ab0946018495f801378a37 Mon Sep 17 00:00:00 2001 From: auria Date: Wed, 15 Feb 2012 01:58:24 +0000 Subject: [PATCH 46/47] massive update : more work to support several challenge difficulties; remove a huge lot of code that is no more needed thanks to the new points system; start work on the challenge-solved cutscene git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10848 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/challenges/challenge.cpp | 49 ++++++-- src/challenges/challenge.hpp | 27 +++-- src/challenges/challenge_data.cpp | 5 +- src/challenges/game_slot.cpp | 106 ++++++++---------- src/challenges/game_slot.hpp | 17 +-- .../dialogs/race_over_dialog.cpp | 8 +- src/states_screens/feature_unlocked.cpp | 65 ++++++++++- src/states_screens/feature_unlocked.hpp | 20 +++- src/states_screens/grand_prix_lose.cpp | 6 +- src/states_screens/grand_prix_win.cpp | 8 +- src/states_screens/race_gui_overworld.cpp | 30 ++--- src/states_screens/race_gui_overworld.hpp | 8 +- src/states_screens/race_result_gui.cpp | 8 +- 13 files changed, 227 insertions(+), 130 deletions(-) diff --git a/src/challenges/challenge.cpp b/src/challenges/challenge.cpp index 6b932232b..11b28c4dd 100644 --- a/src/challenges/challenge.cpp +++ b/src/challenges/challenge.cpp @@ -45,19 +45,50 @@ void Challenge::load(const XMLNode* challengesNode) m_data->getId().c_str()); return; } - - // See if the challenge is solved (it's activated later from the - // unlock_manager). - bool finished=false; - node->get("solved", &finished); + const XMLNode* easy = node->getNode("easy"); + const XMLNode* medium = node->getNode("medium"); + const XMLNode* hard = node->getNode("hard"); + + m_state[0] = CH_INACTIVE; + m_state[1] = CH_INACTIVE; + m_state[2] = CH_INACTIVE; + + if (easy != NULL) + { + bool finished = false; + easy->get("solved", &finished); - m_state = finished ? CH_SOLVED : CH_INACTIVE; + if (finished) m_state[0] = CH_SOLVED; + } + if (medium != NULL) + { + bool finished = false; + medium->get("solved", &finished); + + if (finished) m_state[1] = CH_SOLVED; + } + if (hard != NULL) + { + bool finished = false; + hard->get("solved", &finished); + + if (finished) m_state[2] = CH_SOLVED; + } } // load //----------------------------------------------------------------------------- + + +const wchar_t* boolstr(bool b) +{ + return (b ? L"true" : L"false"); +} + void Challenge::save(XMLWriter& writer) { - writer << L" <" << core::stringw(m_data->getId().c_str()) << L" solved=\"" - << (isSolved() ? L"true" : L"false") << L"\""; - writer << L" />\n"; + writer << L" <" << core::stringw(m_data->getId().c_str()) << L">\n" + << L" \n" + << L" \n" + << L" \n" + << L" getId().c_str()) << L">\n"; } // save diff --git a/src/challenges/challenge.hpp b/src/challenges/challenge.hpp index 432ddb91d..82aca8d4a 100644 --- a/src/challenges/challenge.hpp +++ b/src/challenges/challenge.hpp @@ -30,6 +30,7 @@ #include #include +#include "race/race_manager.hpp" #include "utils/no_copy.hpp" #include "utils/translation.hpp" @@ -46,25 +47,35 @@ class Challenge : public NoCopy private: enum {CH_INACTIVE, // challenge not yet possible CH_ACTIVE, // challenge possible, but not yet solved - CH_SOLVED} m_state; // challenge was solved - + CH_SOLVED} // challenge was solved + m_state[RaceManager::DIFFICULTY_COUNT]; + ChallengeData* m_data; public: - Challenge(ChallengeData* data) : m_state(CH_INACTIVE) - { m_data = data; } + Challenge(ChallengeData* data) + { + m_data = data; + m_state[RaceManager::RD_EASY] = CH_INACTIVE; + m_state[RaceManager::RD_MEDIUM] = CH_INACTIVE; + m_state[RaceManager::RD_HARD] = CH_INACTIVE; + } virtual ~Challenge() {}; void load(const XMLNode* config); void save(XMLWriter& writer); // ------------------------------------------------------------------------ - bool isSolved() const {return m_state==CH_SOLVED; } + bool isSolved(RaceManager::Difficulty d) const {return m_state[d]==CH_SOLVED; } // ------------------------------------------------------------------------ - bool isActive() const {return m_state==CH_ACTIVE; } + bool isSolvedAtAnyDifficulty() const {return m_state[0]==CH_SOLVED || + m_state[1]==CH_SOLVED || + m_state[2]==CH_SOLVED; } // ------------------------------------------------------------------------ - void setSolved() {m_state = CH_SOLVED; } + bool isActive(RaceManager::Difficulty d) const {return m_state[d]==CH_ACTIVE; } // ------------------------------------------------------------------------ - void setActive() {m_state = CH_ACTIVE; } + void setSolved(RaceManager::Difficulty d) {m_state[d] = CH_SOLVED; } + // ------------------------------------------------------------------------ + void setActive(RaceManager::Difficulty d) {m_state[d] = CH_ACTIVE; } // ------------------------------------------------------------------------ ChallengeData* getData() { return m_data; } }; diff --git a/src/challenges/challenge_data.cpp b/src/challenges/challenge_data.cpp index 4ce8ba7a9..d076d7f79 100644 --- a/src/challenges/challenge_data.cpp +++ b/src/challenges/challenge_data.cpp @@ -356,12 +356,13 @@ bool ChallengeData::raceFinished() int d = race_manager->getDifficulty(); + Kart* kart = world->getPlayerKart(0); + if (track_name != m_track_id ) return false; if ((int)world->getNumKarts() < m_num_karts[d] ) return false; - Kart* kart = world->getPlayerKart(0); if (m_energy[d] > 0 && kart->getEnergy() < m_energy[d] ) return false; if (m_position[d] > 0 && kart->getPosition() > m_position[d]) return false; - + // Follow the leader // ----------------- if(m_minor==RaceManager::MINOR_MODE_FOLLOW_LEADER) diff --git a/src/challenges/game_slot.cpp b/src/challenges/game_slot.cpp index 940367273..3d00e9fb2 100644 --- a/src/challenges/game_slot.cpp +++ b/src/challenges/game_slot.cpp @@ -29,35 +29,6 @@ bool GameSlot::isLocked(const std::string& feature) { return m_locked_features.find(feature)!=m_locked_features.end(); } // featureIsLocked - //----------------------------------------------------------------------------- - -const std::vector GameSlot::getUnlockedFeatures() -{ - std::vector out; - - std::map::const_iterator i; - for(i = m_challenges_state.begin(); - i != m_challenges_state.end(); i++) - { - if (i->second->isSolved()) out.push_back(i->second->getData()); - } - - return out; -} -//----------------------------------------------------------------------------- -const std::vector GameSlot::getLockedChallenges() -{ - std::vector out; - - std::map::const_iterator i; - for(i = m_challenges_state.begin(); - i != m_challenges_state.end(); i++) - { - if (!i->second->isSolved()) out.push_back(i->second->getData()); - } - - return out; -} //----------------------------------------------------------------------------- void GameSlot::computeActive() @@ -70,21 +41,53 @@ void GameSlot::computeActive() { // Changed challenge // ----------------- - if((i->second)->isSolved()) + if((i->second)->isSolvedAtAnyDifficulty()) { // The constructor calls computeActive, which actually locks // all features, so unlock the solved ones (and don't try to // save the state, since we are currently reading it) - unlockFeature(i->second, /*save*/ false); + if (i->second->isSolved(RaceManager::RD_EASY)) + { + unlockFeature(i->second, RaceManager::RD_EASY, /*save*/ false); + } + if (i->second->isSolved(RaceManager::RD_MEDIUM)) + { + unlockFeature(i->second, RaceManager::RD_MEDIUM, /*save*/ false); + } + if (i->second->isSolved(RaceManager::RD_HARD)) + { + unlockFeature(i->second, RaceManager::RD_HARD, /*save*/ false); + } m_points++; - continue; + } + else + { + // Otherwise lock the feature + // -------------------------- + lockFeature(i->second); } - // Otherwise lock the feature - // -------------------------- - lockFeature(i->second); - i->second->setActive(); + if (i->second->isSolved(RaceManager::RD_HARD)) + { + // challenge beaten at hardest, nothing more to do here + continue; + } + else if (i->second->isSolved(RaceManager::RD_MEDIUM)) + { + i->second->setActive(RaceManager::RD_HARD); + } + else if (i->second->isSolved(RaceManager::RD_EASY)) + { + i->second->setActive(RaceManager::RD_HARD); + i->second->setActive(RaceManager::RD_MEDIUM); + } + else + { + i->second->setActive(RaceManager::RD_HARD); + i->second->setActive(RaceManager::RD_MEDIUM); + i->second->setActive(RaceManager::RD_EASY); + } } // for i clearUnlocked(); } // computeActive @@ -102,7 +105,7 @@ void GameSlot::lockFeature(Challenge *challenge) //----------------------------------------------------------------------------- -void GameSlot::unlockFeature(Challenge* c, bool do_save) +void GameSlot::unlockFeature(Challenge* c, RaceManager::Difficulty d, bool do_save) { const unsigned int amount = (unsigned int)c->getData()->getFeatures().size(); for(unsigned int n=0; ngetData()); - c->setSolved(); // reset isActive flag + c->setSolved(d); // reset isActive flag // Save the new unlock information if(do_save) unlock_manager->save(); @@ -128,24 +131,6 @@ void GameSlot::unlockFeature(Challenge* c, bool do_save) //----------------------------------------------------------------------------- -std::vector GameSlot::getActiveChallenges() -{ - computeActive(); - - std::vector out; - - std::map::const_iterator i; - for(i = m_challenges_state.begin(); - i != m_challenges_state.end(); i++) - { - if (i->second->isActive()) out.push_back(i->second->getData()); - } - - return out; -} // getActiveChallenges - -//----------------------------------------------------------------------------- - /** This is called when a race is finished. Call all active challenges */ void GameSlot::raceFinished() @@ -156,9 +141,9 @@ void GameSlot::raceFinished() for(i = m_challenges_state.begin(); i != m_challenges_state.end(); i++) { - if(i->second->isActive() && i->second->getData()->raceFinished()) + if(i->second->isActive(race_manager->getDifficulty()) && i->second->getData()->raceFinished()) { - unlockFeature(i->second); + unlockFeature(i->second, race_manager->getDifficulty()); } // if isActive && challenge solved } //race_manager->setCoinTarget(0); //reset @@ -172,10 +157,11 @@ void GameSlot::grandPrixFinished() for(i = m_challenges_state.begin(); i != m_challenges_state.end(); i++) { - if(i->second->isActive() && i->second->getData()->grandPrixFinished()) + if(i->second->isActive(race_manager->getDifficulty()) && + i->second->getData()->grandPrixFinished()) { printf("===== A FEATURE WAS UNLOCKED BECAUSE YOU WON THE GP!! ==\n"); - unlockFeature(i->second); + unlockFeature(i->second, race_manager->getDifficulty()); } } race_manager->setCoinTarget(0); diff --git a/src/challenges/game_slot.hpp b/src/challenges/game_slot.hpp index f6b1da78a..a7c6db1d5 100644 --- a/src/challenges/game_slot.hpp +++ b/src/challenges/game_slot.hpp @@ -25,6 +25,8 @@ #include #include +#include "race/race_manager.hpp" + class ChallengeData; class Challenge; class XMLWriter; @@ -70,26 +72,17 @@ public: /** Returns the list of recently unlocked features (e.g. call at the end of a race to know if any features were unlocked) */ const std::vector - getRecentlyUnlockedFeatures() {return m_unlocked_features;} + getRecentlyCompletedChallenges() {return m_unlocked_features;} /** Clear the list of recently unlocked challenges */ void clearUnlocked () {m_unlocked_features.clear(); } - - /** Returns a complete list of all solved challenges */ - const std::vector getUnlockedFeatures(); - - /** Returns the list of currently inaccessible (locked) challenges */ - const std::vector getLockedChallenges(); - bool isLocked (const std::string& feature); void lockFeature (Challenge *challenge); - void unlockFeature (Challenge* c, bool do_save=true); - - std::vector getActiveChallenges(); - + void unlockFeature (Challenge* c, RaceManager::Difficulty d, bool do_save=true); + void raceFinished (); void grandPrixFinished (); diff --git a/src/states_screens/dialogs/race_over_dialog.cpp b/src/states_screens/dialogs/race_over_dialog.cpp index 5dcf8b7c7..60d612e6e 100644 --- a/src/states_screens/dialogs/race_over_dialog.cpp +++ b/src/states_screens/dialogs/race_over_dialog.cpp @@ -309,7 +309,7 @@ RaceOverDialog::RaceOverDialog(const float percentWidth, } // ---- Buttons at the bottom - if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) + if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0) { const int label_y = m_area.getHeight() - (button_h + margin_between_buttons)*2; @@ -480,14 +480,14 @@ GUIEngine::EventPropagation RaceOverDialog::processEvent(const std::string& even else if (eventSource == "seeunlocked") { std::vector unlocked = - unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); + unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges(); unlock_manager->getCurrentSlot()->clearUnlocked(); FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene::getInstance(); assert(unlocked.size() > 0); - scene->addUnlockedThings(unlocked); + scene->addTrophy(race_manager->getDifficulty()); ModalDialog::dismiss(); @@ -505,7 +505,7 @@ GUIEngine::EventPropagation RaceOverDialog::processEvent(const std::string& even void RaceOverDialog::escapePressed() { - if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) + if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0) { std::string what = "seeunlocked"; processEvent(what); diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp index 33e20130a..c9201839f 100644 --- a/src/states_screens/feature_unlocked.cpp +++ b/src/states_screens/feature_unlocked.cpp @@ -54,6 +54,18 @@ DEFINE_SCREEN_SINGLETON( FeatureUnlockedCutScene ); #pragma mark FeatureUnlockedCutScene::UnlockedThing #endif +FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(std::string model, + irr::core::stringw msg) +{ + m_unlocked_kart = NULL; + m_unlock_message = msg; + m_unlock_model = model; + m_curr_image = -1; +} + +// ------------------------------------------------------------------------------------- + + FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(KartProperties* kart, irr::core::stringw msg) { @@ -125,6 +137,48 @@ void FeatureUnlockedCutScene::loadedFromFile() // ---------------------------------------------------------------------------- +void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty) +{ + core::stringw msg; + switch (difficulty) + { + case RaceManager::RD_EASY: + msg = _("You completed the easy challenge!"); + break; + case RaceManager::RD_MEDIUM: + msg = _("You completed the intermediate challenge!"); + break; + case RaceManager::RD_HARD: + msg = _("You completed the difficult challenge!"); + break; + default: + assert(false); + } + + std::string model; + switch (difficulty) + { + case RaceManager::RD_EASY: + model = file_manager->getModelFile("trophy_bronze.b3d"); + break; + case RaceManager::RD_MEDIUM: + model = file_manager->getModelFile("trophy_silver.b3d"); + break; + case RaceManager::RD_HARD: + model = file_manager->getModelFile("trophy_gold.b3d"); + break; + default: + assert(false); + return; + } + + + m_unlocked_stuff.push_back( new UnlockedThing(model, msg) ); +} + +// ---------------------------------------------------------------------------- +// unused for now, maybe will be useful later? +/* void FeatureUnlockedCutScene::addUnlockedKart(KartProperties* unlocked_kart, irr::core::stringw msg) { @@ -159,7 +213,7 @@ void FeatureUnlockedCutScene::addUnlockedPictures(std::vector 0) + { + m_unlocked_stuff[n].m_root_gift_node = irr_driver->addMesh( irr_driver->getMesh(m_unlocked_stuff[n].m_unlock_model) ); + } + else if (m_unlocked_stuff[n].m_unlocked_kart != NULL) { KartModel *kart_model = m_unlocked_stuff[n].m_unlocked_kart->getKartModelCopy(); @@ -499,6 +557,8 @@ void FeatureUnlockedCutScene::onUpdate(float dt, // ---------------------------------------------------------------------------- +// unused for now... maybe this could could useful later? +/* void FeatureUnlockedCutScene::addUnlockedThings(const std::vector unlocked) { for (unsigned int n=0; n m_pictures; /** Will be set if this unlocked thing is a picture */ @@ -59,6 +62,8 @@ class FeatureUnlockedCutScene : public GUIEngine::Screen, public GUIEngine::Scre irr::core::stringw m_unlock_message; + UnlockedThing(std::string model, irr::core::stringw msg); + UnlockedThing(KartProperties* kart, irr::core::stringw msg); /** @@ -136,21 +141,28 @@ public: /** Call before showing up the screen to make a kart come out of the chest. 'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */ - void addUnlockedKart(KartProperties* unlocked_kart, irr::core::stringw msg); + // unused for now, maybe will be useful later? + //void addUnlockedKart(KartProperties* unlocked_kart, irr::core::stringw msg); /** Call before showing up the screen to make a picture come out of the chest 'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */ - void addUnlockedPicture(irr::video::ITexture* picture, float w, float h, irr::core::stringw msg); + // unused for now, maybe will be useful later? + //void addUnlockedPicture(irr::video::ITexture* picture, float w, float h, irr::core::stringw msg); /** Call before showing up the screen to make a picture slideshow come out of the chest 'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */ - void addUnlockedPictures(std::vector pictures, - float w, float h, irr::core::stringw msg); + // unused for now, maybe will be useful later? + //void addUnlockedPictures(std::vector pictures, + // float w, float h, irr::core::stringw msg); /** Call before showing up the screen to make whatever the passed challenges unlocked * come out of the chest */ + // unused for now... maybe this could could useful later? + /* void addUnlockedThings(const std::vector unlocked); + */ + void addTrophy(RaceManager::Difficulty difficulty); /** override from base class to handle escape press */ virtual bool onEscapePressed(); diff --git a/src/states_screens/grand_prix_lose.cpp b/src/states_screens/grand_prix_lose.cpp index 55cc581c6..514f0aa03 100644 --- a/src/states_screens/grand_prix_lose.cpp +++ b/src/states_screens/grand_prix_lose.cpp @@ -271,17 +271,17 @@ void GrandPrixLose::eventCallback(GUIEngine::Widget* widget, // un-set the GP mode so that after unlocking, it doesn't try to continue the GP race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE); - if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) + if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0) { std::vector unlocked = - unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); + unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges(); unlock_manager->getCurrentSlot()->clearUnlocked(); FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene::getInstance(); assert(unlocked.size() > 0); - scene->addUnlockedThings(unlocked); + scene->addTrophy(race_manager->getDifficulty()); StateManager::get()->replaceTopMostScreen(scene); } diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index cee245a75..b1a005805 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -95,7 +95,7 @@ void GrandPrixWin::loadedFromFile() void GrandPrixWin::init() { Screen::init(); - if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) + if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0) { const core::dimension2d& frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize(); @@ -393,17 +393,17 @@ void GrandPrixWin::eventCallback(GUIEngine::Widget* widget, // un-set the GP mode so that after unlocking, it doesn't try to continue the GP race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE); - if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) + if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0) { std::vector unlocked = - unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); + unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges(); unlock_manager->getCurrentSlot()->clearUnlocked(); FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene::getInstance(); assert(unlocked.size() > 0); - scene->addUnlockedThings(unlocked); + scene->addTrophy(race_manager->getDifficulty()); StateManager::get()->replaceTopMostScreen(scene); } diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 095b789aa..6b1bd76d9 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -51,7 +51,9 @@ using namespace irr; const int LOCKED = 0; const int OPEN = 1; -const int COMPLETED = 2; +const int COMPLETED_EASY = 2; +const int COMPLETED_MEDIUM = 3; +const int COMPLETED_HARD = 4; /** 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 @@ -60,8 +62,10 @@ const int COMPLETED = 2; RaceGUIOverworld::RaceGUIOverworld() { m_enabled = true; - m_trophy = irr_driver->getTexture( file_manager->getTextureFile("cup_gold.png") ); - + m_trophy1 = irr_driver->getTexture( file_manager->getTextureFile("cup_bronze.png") ); + m_trophy2 = irr_driver->getTexture( file_manager->getTextureFile("cup_silver.png") ); + m_trophy3 = irr_driver->getTexture( file_manager->getTextureFile("cup_gold.png") ); + const float scaling = irr_driver->getFrameSize().Height / 420.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))); @@ -101,18 +105,14 @@ RaceGUIOverworld::RaceGUIOverworld() gui::ScalableFont* font = GUIEngine::getFont(); m_trophy_points_width = font->getDimension(L"100").Width; - const std::vector& v = unlock_manager->getCurrentSlot()->getLockedChallenges(); - for (unsigned int n=0; ngetTexture( file_manager->getTextureFile("gui_lock.png") ); m_open_challenge = irr_driver->getTexture( file_manager->getGUIDir() + "challenge.png" ); m_icons[0] = m_lock; m_icons[1] = m_open_challenge; - m_icons[2] = m_trophy; + m_icons[2] = m_trophy1; + m_icons[3] = m_trophy2; + m_icons[4] = m_trophy3; } // RaceGUIOverworld //----------------------------------------------------------------------------- @@ -217,9 +217,9 @@ void RaceGUIOverworld::drawTrophyPoints() const int size = UserConfigParams::m_width/20; core::rect dest(pos.UpperLeftCorner.X - size - 5, pos.UpperLeftCorner.Y, pos.UpperLeftCorner.X - 5, pos.UpperLeftCorner.Y + size); - core::rect source(core::position2di(0, 0), m_trophy->getSize()); + core::rect source(core::position2di(0, 0), m_trophy3->getSize()); - irr_driver->getVideoDriver()->draw2DImage(m_trophy, dest, source, NULL, + irr_driver->getVideoDriver()->draw2DImage(m_trophy3, dest, source, NULL, NULL, true /* alpha */); pos.LowerRightCorner.Y = dest.LowerRightCorner.Y; @@ -321,8 +321,10 @@ void RaceGUIOverworld::drawGlobalMiniMap() int state = (challenges[n].m_force_field.m_is_locked ? LOCKED : OPEN); const Challenge* c = unlock_manager->getCurrentSlot()->getChallenge(challenges[n].m_challenge_id); - if (c->isSolved()) state = COMPLETED; - + if (c->isSolved(RaceManager::RD_HARD)) state = COMPLETED_HARD; + else if (c->isSolved(RaceManager::RD_MEDIUM)) state = COMPLETED_MEDIUM; + else if (c->isSolved(RaceManager::RD_EASY)) state = COMPLETED_EASY; + const core::rect source(core::position2d(0,0), m_icons[state]->getOriginalSize()); diff --git a/src/states_screens/race_gui_overworld.hpp b/src/states_screens/race_gui_overworld.hpp index d16e68d95..97c185bb9 100644 --- a/src/states_screens/race_gui_overworld.hpp +++ b/src/states_screens/race_gui_overworld.hpp @@ -61,11 +61,13 @@ private: /** The mini map of the track. */ video::ITexture *m_mini_map; - video::ITexture *m_trophy; + video::ITexture *m_trophy1; + video::ITexture *m_trophy2; + video::ITexture *m_trophy3; video::ITexture *m_lock; video::ITexture *m_open_challenge; - video::ITexture* m_icons[3]; + video::ITexture* m_icons[5]; /** The size of a single marker on the screen for AI karts, * need not be a power of 2. */ @@ -95,8 +97,6 @@ private: int m_trophy_points_width; - std::set m_locked_challenges; - /* Display informat for one player on the screen. */ void drawEnergyMeter (int x, int y, const Kart *kart, const core::recti &viewport, diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 90fe0ab2c..d0997fca2 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -90,7 +90,7 @@ void RaceResultGUI::enableAllButtons() // If something was unlocked // ------------------------- - int n = unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size(); + int n = unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size(); if(n>0) { top->setText(n==1 ? _("See unlocked feature") @@ -137,7 +137,7 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, // If something was unlocked, the 'continue' button was // actually used to display "Show unlocked feature(s)" text. // --------------------------------------------------------- - int n = unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size(); + int n = unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size(); if(n>0) { if(name=="top") @@ -148,11 +148,11 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, } std::vector unlocked = - unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); + unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges(); unlock_manager->getCurrentSlot()->clearUnlocked(); FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene::getInstance(); - scene->addUnlockedThings(unlocked); + scene->addTrophy(race_manager->getDifficulty()); StateManager::get()->popMenu(); StateManager::get()->pushScreen(scene); World::deleteWorld(); From c92df26ea6491c4921892e6c858ed556527154a0 Mon Sep 17 00:00:00 2001 From: auria Date: Wed, 15 Feb 2012 02:21:23 +0000 Subject: [PATCH 47/47] Show which challenges have been solved in the dialog git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10849 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/challenges/challenge.hpp | 4 ++- .../dialogs/select_challenge.cpp | 34 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/challenges/challenge.hpp b/src/challenges/challenge.hpp index 82aca8d4a..6f5a7daca 100644 --- a/src/challenges/challenge.hpp +++ b/src/challenges/challenge.hpp @@ -77,6 +77,8 @@ public: // ------------------------------------------------------------------------ void setActive(RaceManager::Difficulty d) {m_state[d] = CH_ACTIVE; } // ------------------------------------------------------------------------ - ChallengeData* getData() { return m_data; } + + ChallengeData* getData() { return m_data; } + const ChallengeData* getData() const { return m_data; } }; #endif diff --git a/src/states_screens/dialogs/select_challenge.cpp b/src/states_screens/dialogs/select_challenge.cpp index 1db9ff8de..9fbf35be7 100644 --- a/src/states_screens/dialogs/select_challenge.cpp +++ b/src/states_screens/dialogs/select_challenge.cpp @@ -18,9 +18,11 @@ #include "challenges/unlock_manager.hpp" #include "config/user_config.hpp" #include "guiengine/engine.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" #include "guiengine/widgets/label_widget.hpp" #include "input/device_manager.hpp" #include "input/input_manager.hpp" +#include "io/file_manager.hpp" #include "modes/world.hpp" #include "network/network_manager.hpp" #include "race/race_manager.hpp" @@ -79,16 +81,38 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth, getWidget("expert")->setFocusForPlayer(PLAYER_ID_GAME_MASTER); break; } - - const ChallengeData* c = unlock_manager->getChallenge(challenge_id); + const Challenge* c = unlock_manager->getCurrentSlot()->getChallenge(challenge_id); + + if (c->isSolved(RaceManager::RD_EASY)) + { + IconButtonWidget* btn = getWidget("novice"); + btn->setImage(file_manager->getTextureFile("cup_bronze.png").c_str(), + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + } + + if (c->isSolved(RaceManager::RD_MEDIUM)) + { + IconButtonWidget* btn = getWidget("intermediate"); + btn->setImage(file_manager->getTextureFile("cup_silver.png").c_str(), + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + } + + if (c->isSolved(RaceManager::RD_HARD)) + { + IconButtonWidget* btn = getWidget("expert"); + btn->setImage(file_manager->getTextureFile("cup_gold.png").c_str(), + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + } + + LabelWidget* novice_label = getWidget("novice_label"); LabelWidget* medium_label = getWidget("intermediate_label"); LabelWidget* expert_label = getWidget("difficult_label"); - novice_label->setText( getLabel(RaceManager::RD_EASY, c), false ); - medium_label->setText( getLabel(RaceManager::RD_MEDIUM, c), false ); - expert_label->setText( getLabel(RaceManager::RD_HARD, c), false ); + novice_label->setText( getLabel(RaceManager::RD_EASY, c->getData()), false ); + medium_label->setText( getLabel(RaceManager::RD_MEDIUM, c->getData()), false ); + expert_label->setText( getLabel(RaceManager::RD_HARD, c->getData()), false ); } // ----------------------------------------------------------------------------