diff --git a/data/gui/mode_easter.png b/data/gui/mode_easter.png new file mode 100644 index 000000000..08e2d4ee3 Binary files /dev/null and b/data/gui/mode_easter.png differ diff --git a/data/items.xml b/data/items.xml index 8c1b19905..32c1110f2 100644 --- a/data/items.xml +++ b/data/items.xml @@ -9,5 +9,7 @@ handled as one, so list it here --> + + diff --git a/data/models/easter_egg-icon.png b/data/models/easter_egg-icon.png new file mode 100644 index 000000000..b3ed0fe91 Binary files /dev/null and b/data/models/easter_egg-icon.png differ diff --git a/data/models/easter_egg.b3d b/data/models/easter_egg.b3d new file mode 100644 index 000000000..7e44c955a Binary files /dev/null and b/data/models/easter_egg.b3d differ diff --git a/data/stk_config.xml b/data/stk_config.xml index 018c465fb..2a0264751 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -110,8 +110,9 @@ - + Order: giftbox, banana, big-nitro, small-nitro, bubble-gum, trigger, + nolok-bubble-gum, easter egg --> + diff --git a/sources.cmake b/sources.cmake index 98bf4e1cd..63924f065 100644 --- a/sources.cmake +++ b/sources.cmake @@ -121,6 +121,7 @@ src/main.cpp src/main_loop.cpp src/modes/cutscene_world.cpp src/modes/demo_world.cpp +src/modes/easter_egg_hunt.cpp src/modes/follow_the_leader.cpp src/modes/game_tutorial.cpp src/modes/linear_world.cpp @@ -367,6 +368,7 @@ src/karts/skidding_properties.hpp src/main_loop.hpp src/modes/cutscene_world.hpp src/modes/demo_world.hpp +src/modes/easter_egg_hunt.hpp src/modes/follow_the_leader.hpp src/modes/game_tutorial.hpp src/modes/linear_world.hpp diff --git a/src/ide/vc9/supertuxkart.vcproj b/src/ide/vc9/supertuxkart.vcproj index a7021c81f..39fe03e28 100644 --- a/src/ide/vc9/supertuxkart.vcproj +++ b/src/ide/vc9/supertuxkart.vcproj @@ -700,6 +700,10 @@ RelativePath="..\..\modes\demo_world.cpp" > + + @@ -1886,6 +1890,10 @@ RelativePath="..\..\modes\demo_world.hpp" > + + diff --git a/src/items/item.cpp b/src/items/item.cpp index 83996fb5e..bd4d3f109 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -24,6 +24,7 @@ #include "graphics/irr_driver.hpp" #include "graphics/lod_node.hpp" #include "karts/abstract_kart.hpp" +#include "modes/easter_egg_hunt.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" #include "tracks/track.hpp" @@ -109,16 +110,23 @@ void Item::initItem(ItemType type, const Vec3 &xyz) m_deactive_time = 0; m_time_till_return = 0.0f; // not strictly necessary, see isCollected() m_emitter = NULL; - m_rotate = (type!=ITEM_BUBBLEGUM) && (type!=ITEM_TRIGGER); - m_disappear_counter = m_type==ITEM_BUBBLEGUM - ? stk_config->m_bubble_gum_counter - : -1 ; + m_rotate = (type!=ITEM_BUBBLEGUM) && (type!=ITEM_TRIGGER) && + (type!=ITEM_EASTER_EGG); + switch(m_type) + { + case ITEM_BUBBLEGUM: + m_disappear_counter = stk_config->m_bubble_gum_counter; break; + case ITEM_EASTER_EGG: + m_disappear_counter = -1; break; + default: + m_disappear_counter = -1; + } // Now determine in which quad this item is, and its distance // from the center within this quad. m_graph_node = QuadGraph::UNKNOWN_SECTOR; QuadGraph* currentQuadGraph = QuadGraph::get(); - // Check that QuadGraph exist ( it might not in battle mode for eg) + // Check that QuadGraph exist (it might not in battle mode for eg) if (currentQuadGraph != NULL) { QuadGraph::get()->findRoadSector(xyz, &m_graph_node); @@ -236,9 +244,15 @@ void Item::reset() m_collected = false; m_time_till_return = 0.0f; m_deactive_time = 0.0f; - m_disappear_counter = m_type==ITEM_BUBBLEGUM - ? stk_config->m_bubble_gum_counter - : -1 ; + switch(m_type) + { + case ITEM_BUBBLEGUM: + m_disappear_counter = stk_config->m_bubble_gum_counter; break; + case ITEM_EASTER_EGG: + m_disappear_counter = -1; break; + default: + m_disappear_counter = -1; + } if(m_original_type!=ITEM_NONE) { setType(m_original_type); @@ -319,7 +333,18 @@ void Item::collected(const AbstractKart *kart, float t) { m_collected = true; m_event_handler = kart; - if(m_type==ITEM_BUBBLEGUM && m_disappear_counter>0) + if(m_type==ITEM_EASTER_EGG) + { + m_time_till_return=99999; + EasterEggHunt *world = dynamic_cast(World::getWorld()); + assert(world); + world->collectedEasterEgg(kart); + if (m_node != NULL) + { + m_node->setVisible(false); + } + } + else if(m_type==ITEM_BUBBLEGUM && m_disappear_counter>0) { m_disappear_counter --; // Deactivates the item for a certain amount of time. It is used to diff --git a/src/items/item.hpp b/src/items/item.hpp index bd577ac94..7ce352796 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -73,7 +73,9 @@ public: ITEM_NITRO_SMALL, ITEM_BUBBLEGUM, ITEM_BUBBLEGUM_NOLOK, - + + /** For easter egg mode only. */ + ITEM_EASTER_EGG, /** An invisible item that can be used to trigger some behavior when * approaching a point */ diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 5f56cf09e..f6c51bda0 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -77,8 +77,8 @@ void ItemManager::loadDefaultItemMeshes() item_names[Item::ITEM_NITRO_BIG ] = "nitro-big"; item_names[Item::ITEM_NITRO_SMALL] = "nitro-small"; item_names[Item::ITEM_TRIGGER ] = "trigger"; - item_names[Item::ITEM_BUBBLEGUM_NOLOK] = "bubblegum-nolok"; + item_names[Item::ITEM_EASTER_EGG ] = "easter-egg"; const std::string file_name = file_manager->getDataFile("items.xml"); const XMLNode *root = file_manager->createXMLTree(file_name); diff --git a/src/items/projectile_manager.cpp b/src/items/projectile_manager.cpp index f8e4f25ef..cdb75dde9 100644 --- a/src/items/projectile_manager.cpp +++ b/src/items/projectile_manager.cpp @@ -85,7 +85,7 @@ void ProjectileManager::update(float dt) he = next; } // Update this hit effect. If it can be removed, remove it. - if((*he)->updateAndDelete(dt)) + else if((*he)->updateAndDelete(dt)) { delete *he; HitEffects::iterator next = m_active_hit_effects.erase(he); diff --git a/src/karts/controller/ai_properties.cpp b/src/karts/controller/ai_properties.cpp index 2cc8366ba..fb2ccfd5d 100644 --- a/src/karts/controller/ai_properties.cpp +++ b/src/karts/controller/ai_properties.cpp @@ -27,13 +27,7 @@ float AIProperties::UNDEFINED = -99.9f; */ AIProperties::AIProperties(RaceManager::Difficulty difficulty) { - switch(difficulty) - { - case RaceManager::DIFFICULTY_EASY: m_ident="easy"; break; - case RaceManager::DIFFICULTY_MEDIUM: m_ident="medium"; break; - case RaceManager::DIFFICULTY_HARD: m_ident="hard"; break; - default: m_ident=""; break; - } + m_ident = race_manager->getDifficultyAsString(difficulty); m_max_item_angle = UNDEFINED; m_max_item_angle_high_speed = UNDEFINED; diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index 112672762..30ceaee6a 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -69,6 +69,9 @@ void Moveable::updateGraphics(float dt, const Vec3& offset_xyz, Vec3 xyz=getXYZ()+offset_xyz; m_node->setPosition(xyz.toIrrVector()); btQuaternion r_all = getRotation()*rotation; + if(btFuzzyZero(r_all.getX()) && btFuzzyZero(r_all.getY()-0.70710677f) && + btFuzzyZero(r_all.getZ()) && btFuzzyZero(r_all.getW()-0.70710677f) ) + r_all.setX(0.000001f); Vec3 hpr; hpr.setHPR(r_all); m_node->setRotation(hpr.toIrrHPR()); diff --git a/src/modes/easter_egg_hunt.cpp b/src/modes/easter_egg_hunt.cpp new file mode 100644 index 000000000..5104bd09c --- /dev/null +++ b/src/modes/easter_egg_hunt.cpp @@ -0,0 +1,203 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2012 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "modes/easter_egg_hunt.hpp" + +#include "io/file_manager.hpp" +#include "karts/abstract_kart.hpp" +#include "tracks/track.hpp" + +//----------------------------------------------------------------------------- +/** Constructor. Sets up the clock mode etc. + */ +EasterEggHunt::EasterEggHunt() : WorldWithRank() +{ + WorldStatus::setClockMode(CLOCK_CHRONO); + m_use_highscores = false; + m_eggs_found = 0; +} // EasterEggHunt + +//----------------------------------------------------------------------------- +/** Initialises the easer egg hunt. + */ +void EasterEggHunt::init() +{ + WorldWithRank::init(); + m_display_rank = false; + + // check for possible problems if AI karts were incorrectly added + if(getNumKarts() > race_manager->getNumPlayers()) + { + fprintf(stderr, "No AI exists for this game mode\n"); + exit(1); + } + + const unsigned int kart_amount = m_karts.size(); + m_eggs_collected.resize(m_karts.size(), 0); + +} // EasterEggHunt + +//----------------------------------------------------------------------------- +/** Destructor. Clears all internal data structures, and removes the tire mesh + * from the mesh cache. + */ +EasterEggHunt::~EasterEggHunt() +{ +} // ~EasterEggHunt + +//----------------------------------------------------------------------------- +/** Check if a file easter_eggs.xml exists in the track directory, and if so + * loads that file and makes the easter egg mode available for this track. +*/ +void EasterEggHunt::readData(const std::string &filename) +{ + XMLNode *easter = file_manager->createXMLTree(filename); + if(!easter) + return; + + if(easter->getName()!="EasterEggHunt") + { + printf("Can't load easter egg file '%s' - no EasterEggHunt element.", + filename); + delete easter; + return; + } + + const XMLNode *data = NULL; + std::string difficulty_name; + RaceManager::Difficulty diff = race_manager->getDifficulty(); + for(unsigned int i=0; igetDifficultyAsString(diff); + data = easter->getNode(difficulty_name); + if(data) break; + diff = (RaceManager::Difficulty)(diff+1); + if(diff==RaceManager::DIFFICULTY_LAST) + diff = RaceManager::DIFFICULTY_FIRST; + } + if(!data) + { + delete easter; + return; + } + + m_time_limit = 9999; + data->get("time-limit", &m_time_limit); + + m_number_of_eggs = 0; + for(unsigned int i=0; igetNumNodes(); i++) + { + const XMLNode *egg = data->getNode(i); + if(egg->getName()!="easter-egg") + { + printf("Unknown node '%s' in easter egg level '%s' - ignored.\n", + egg->getName().c_str(), difficulty_name.c_str()); + continue; + } + World::getTrack()->itemCommand(egg); + m_number_of_eggs++; + } // for i getWorldKartId()]++; + m_eggs_found++; +} // collectedEasterEgg + +//----------------------------------------------------------------------------- +/** Update the world and the track. + * \param dt Time step size. + */ +void EasterEggHunt::update(float dt) +{ + WorldWithRank::update(dt); + WorldWithRank::updateTrack(dt); +} // update + +//----------------------------------------------------------------------------- +/** The hunt is over once all eggs are found. + */ +bool EasterEggHunt::isRaceOver() +{ + if(m_eggs_found == m_number_of_eggs) + return true; + if(m_time<0) + return true; + return false; +} // isRaceOver + +//----------------------------------------------------------------------------- +/** Called then a battle is restarted. + */ +void EasterEggHunt::restartRace() +{ + WorldWithRank::restartRace(); + + for(unsigned int i=0; i *info) +{ + const unsigned int kart_amount = getNumKarts(); + for(unsigned int i = 0; i < kart_amount ; i++) + { + RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i]; + std::ostringstream o; + //I18n: number of collected eggs / overall number of eggs + rank_info.m_text = StringUtils::insertValues(_("Eggs: %d / %d"), + m_eggs_collected[i], + m_number_of_eggs); + rank_info.m_color = video::SColor(255, 255, 255, 255); + } +} // getKartDisplayInfo + +//----------------------------------------------------------------------------- +/** Moves a kart to its rescue position. + * \param kart The kart that was rescued. + */ +void EasterEggHunt::moveKartAfterRescue(AbstractKart* kart) +{ + int start_position = kart->getInitialPosition(); + btTransform start_pos = getTrack()->getStartTransform(start_position-1); + + kart->getBody()->setCenterOfMassTransform(start_pos); + +} // moveKartAfterRescue diff --git a/src/modes/easter_egg_hunt.hpp b/src/modes/easter_egg_hunt.hpp new file mode 100755 index 000000000..980e9c238 --- /dev/null +++ b/src/modes/easter_egg_hunt.hpp @@ -0,0 +1,74 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2012 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef EASTER_EGG_HUNT_HPP +#define EASTER_EGG_HUNT_HPP + +#include "modes/world_with_rank.hpp" +#include "states_screens/race_gui_base.hpp" + +#include +#include + +class AbstractKart; + +/** + * \brief An implementation of World to provide an easter egg hunt like mode + * \ingroup modes + */ +class EasterEggHunt: public WorldWithRank +{ +private: + /** Keeps track of how many eggs each kart has found. */ + std::vector m_eggs_collected; + + /** A time limit for the hunt. */ + float m_time_limit; + + /** Overall number of easter eggs. */ + int m_number_of_eggs; + + /** Number of eggs found so far. */ + int m_eggs_found; +public: + EasterEggHunt(); + virtual ~EasterEggHunt(); + + virtual void init(); + + virtual bool isRaceOver(); + + // overriding World methods + virtual void restartRace(); + + virtual bool raceHasLaps(){ return false; } + virtual void moveKartAfterRescue(AbstractKart* kart); + + virtual const std::string& getIdent() const; + + virtual void update(float dt); + virtual void getKartsDisplayInfo( + std::vector *info); + + void updateKartRanks(); + void collectedEasterEgg(const AbstractKart *kart); + void readData(const std::string &filename); +}; // EasterEggHunt + + +#endif diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 238bafaa0..ef0db49fc 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -422,9 +422,7 @@ void LinearWorld::getKartsDisplayInfo( AbstractKart* kart = m_karts[i]; // reset color - rank_info.r = 1.0; - rank_info.g = 1.0; - rank_info.b = 1.0; + rank_info.m_color = video::SColor(255, 255, 255, 255); rank_info.lap = -1; if(kart->isEliminated()) continue; @@ -486,12 +484,15 @@ void LinearWorld::getKartsDisplayInfo( if(kart_info.m_race_lap>=numLaps) { // kart is finished, display in green - rank_info.g = rank_info.b = 0; + rank_info.m_color.setGreen(0); + rank_info.m_color.setBlue(0); } else if(kart_info.m_race_lap>=0 && numLaps>1) { - rank_info.g = rank_info.b = - 1.0f-(float)kart_info.m_race_lap/((float)numLaps-1.0f); + int col = (int)(255*(1.0f-(float)kart_info.m_race_lap + /((float)numLaps-1.0f) )); + rank_info.m_color.setBlue(col); + rank_info.m_color.setGreen(col); } } // next kart @@ -561,22 +562,15 @@ void LinearWorld::moveKartAfterRescue(AbstractKart* kart) info.getSector()->rescue(); int sector = info.getSector()->getCurrentGraphNode(); - kart->setXYZ( QuadGraph::get() - ->getQuadOfNode(sector).getCenter()); - - btQuaternion heading(btVector3(0.0f, 1.0f, 0.0f), - m_track->getAngle(sector) ); - kart->setRotation(heading); // A certain epsilon is added here to the Z coordinate, in case // that the drivelines are somewhat under the track. Otherwise, the // kart might be placed a little bit under the track, triggering // a rescue, ... (experimentally found value) float epsilon = 0.5f * kart->getKartHeight(); - + const Vec3 &xyz = QuadGraph::get()->getQuadOfNode(sector).getCenter(); btTransform pos; - pos.setOrigin(kart->getXYZ()+btVector3(0, kart->getKartHeight() + epsilon, - 0)); + pos.setOrigin(xyz+btVector3(0, kart->getKartHeight() + epsilon,0)); pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), m_track->getAngle(sector))); diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 47432066e..522d142f6 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -438,24 +438,16 @@ void ThreeStrikesBattle::getKartsDisplayInfo( switch(m_kart_info[i].m_lives) { case 3: - rank_info.r = 0.0; - rank_info.g = 1.0; - rank_info.b = 0.0; + rank_info.m_color = video::SColor(255, 0, 255, 0); break; case 2: - rank_info.r = 1.0; - rank_info.g = 0.9f; - rank_info.b = 0.0; + rank_info.m_color = video::SColor(255, 255, 229, 0); break; case 1: - rank_info.r = 1.0; - rank_info.g = 0.0; - rank_info.b = 0.0; + rank_info.m_color = video::SColor(255, 255, 0, 0); break; case 0: - rank_info.r = 0.5; - rank_info.g = 0.5; - rank_info.b = 0.5; + rank_info.m_color = video::SColor(128, 128, 128, 0); break; } diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 03bf54af1..6d35d1c02 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -138,7 +138,7 @@ void World::init() // Load the track models - this must be done before the karts so that the // karts can be positioned properly on (and not in) the tracks. - m_track->loadTrackModel(this, race_manager->getReverseTrack()); + m_track->loadTrackModel(race_manager->getReverseTrack()); for(unsigned int i=0; igetMajorMode() != RaceManager::MAJOR_MODE_SINGLE) + if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX) { GUIEngine::RibbonWidget* choice_ribbon = getWidget("choiceribbon"); diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index bc9040b95..758084b85 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -807,14 +807,15 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) int lap = info.lap; // In battle mode there is no distance along track etc. - if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES) + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES || + race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG) { x = x_base; y = previous_y+ICON_PLAYER_WIDTH+2; } else { - LinearWorld *linear_world = (LinearWorld*)(World::getWorld()); + LinearWorld *linear_world = (LinearWorld*)(World::getWorld()); float distance = linear_world->getDistanceDownTrackForKart(kart_id) + linear_world->getTrack()->getTrackLength()*lap; @@ -870,15 +871,12 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) if (m_kart_display_infos[kart_id].m_text.size() > 0) { - video::SColor color = video::SColor(255, - (int)(255*info.r), - (int)(255*info.g), - (int)(255*info.b) ); core::rect pos(x+ICON_PLAYER_WIDTH, y+5, x+ICON_PLAYER_WIDTH, y+5); core::stringw s=info.m_text.c_str(); - font->draw(s.c_str(), pos, color, false, false, NULL, true /* ignore RTL */); + font->draw(s.c_str(), pos, info.m_color, false, false, NULL, + true /* ignore RTL */); } if (info.special_title.size() > 0) @@ -887,7 +885,8 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) core::rect pos(x+ICON_PLAYER_WIDTH, y+5, x+ICON_PLAYER_WIDTH, y+5); core::stringw s(info.special_title.c_str()); - font->draw(s.c_str(), pos, color, false, false, NULL, true /* ignore RTL */); + font->draw(s.c_str(), pos, info.m_color, false, false, NULL, + true /* ignore RTL */); } // draw icon diff --git a/src/states_screens/race_gui_base.hpp b/src/states_screens/race_gui_base.hpp index 72331cf26..bbe6ad4a5 100644 --- a/src/states_screens/race_gui_base.hpp +++ b/src/states_screens/race_gui_base.hpp @@ -53,16 +53,16 @@ public: */ struct KartIconDisplayInfo { - /** text to display next to icon, if any */ + /** Text to display next to icon, if any. */ core::stringw m_text; - /** text color, if any text */ - float r, g, b; + /** Text color, if any text. */ + video::SColor m_color; - /** if this kart has a special title, e.g. "leader" in follow-the-leader */ + /** If this kart has a special title, e.g. "leader" in follow-the-leader. */ core::stringw special_title; - /** Current lap of this kart, or -1 if irrelevant */ + /** Current lap of this kart, or -1 if irrelevant. */ int lap; }; // KartIconDisplayInfo diff --git a/src/states_screens/race_setup_screen.cpp b/src/states_screens/race_setup_screen.cpp index 970dd4895..1f74655af 100644 --- a/src/states_screens/race_setup_screen.cpp +++ b/src/states_screens/race_setup_screen.cpp @@ -29,10 +29,11 @@ #include "states_screens/race_setup_screen.hpp" -const int CONFIG_CODE_NORMAL = 0; +const int CONFIG_CODE_NORMAL = 0; const int CONFIG_CODE_TIMETRIAL = 1; -const int CONFIG_CODE_FTL = 2; -const int CONFIG_CODE_3STRIKES = 3; +const int CONFIG_CODE_FTL = 2; +const int CONFIG_CODE_3STRIKES = 3; +const int CONFIG_CODE_EASTER = 4; using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( RaceSetupScreen ); @@ -53,19 +54,19 @@ public: // game mode changed!! m_parent->onGameModeChanged(); } -}; +}; // GameModeRibbonListener // ----------------------------------------------------------------------------- RaceSetupScreen::RaceSetupScreen() : Screen("racesetup.stkgui") { -} +} // RaceSetupScreen // ----------------------------------------------------------------------------- void RaceSetupScreen::loadedFromFile() { -} +} // loadedFromFile // ----------------------------------------------------------------------------- @@ -127,6 +128,13 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, con race_manager->setNumKarts( race_manager->getNumLocalPlayers() ); // no AI karts; StateManager::get()->pushScreen( ArenasScreen::getInstance() ); } + else if (selectedMode == IDENT_EASTER) + { + race_manager->setMinorMode(RaceManager::MINOR_MODE_EASTER_EGG); + UserConfigParams::m_game_mode = CONFIG_CODE_EASTER; + race_manager->setNumKarts( race_manager->getNumLocalPlayers() ); // no AI karts; + StateManager::get()->pushScreen( TracksScreen::getInstance() ); + } else if (selectedMode == "locked") { unlock_manager->playLockSound(); @@ -142,7 +150,7 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, con { StateManager::get()->escapePressed(); } -} +} // eventCallback // ----------------------------------------------------------------------------- @@ -167,7 +175,7 @@ void RaceSetupScreen::onGameModeChanged() { kartamount->setActivated(); } -} +} // onGameModeChanged // ----------------------------------------------------------------------------- @@ -228,7 +236,15 @@ void RaceSetupScreen::init() name4 += _("Hit others with weapons until they lose all their lives. (Only in multiplayer games)"); w2->addItem( name4, IDENT_STRIKES, RaceManager::getIconOf(RaceManager::MINOR_MODE_3_STRIKES)); } - + { + irr::core::stringw name1 = irr::core::stringw( + RaceManager::getNameOf(RaceManager::MINOR_MODE_EASTER_EGG)) + L"\n"; + //FIXME: avoid duplicating descriptions from the help menu! + name1 += _("Find all Easter Eggs"); + + w2->addItem( name1, IDENT_EASTER, + RaceManager::getIconOf(RaceManager::MINOR_MODE_EASTER_EGG)); + } w2->updateItemDisplay(); @@ -247,10 +263,13 @@ void RaceSetupScreen::init() case CONFIG_CODE_3STRIKES : w2->setSelection(IDENT_STRIKES, PLAYER_ID_GAME_MASTER, true); break; + case CONFIG_CODE_EASTER : + w2->setSelection(IDENT_EASTER, PLAYER_ID_GAME_MASTER, true); + break; } m_mode_listener = new GameModeRibbonListener(this); w2->registerHoverListener(m_mode_listener); -} +} // init // ----------------------------------------------------------------------------- diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 391872cea..d84ad71e3 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -286,6 +286,9 @@ void TracksScreen::buildTrackList() for (int n=0; ngetTrack( n ); + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG + && !curr->hasEasterEggs()) + continue; if (curr->isArena()) continue; if (curr->isInternal()) continue; @@ -313,6 +316,9 @@ void TracksScreen::buildTrackList() for (int n=0; ngetTrack( curr_group[n] ); + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG + && !curr->hasEasterEggs()) + continue; if (curr->isArena()) continue; if (curr->isInternal()) continue; diff --git a/src/tracks/check_lap.cpp b/src/tracks/check_lap.cpp index 20d636de0..3471107d7 100644 --- a/src/tracks/check_lap.cpp +++ b/src/tracks/check_lap.cpp @@ -60,8 +60,12 @@ void CheckLap::reset(const Track &track) bool CheckLap::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, int indx) { float track_length = World::getWorld()->getTrack()->getTrackLength(); - float current_distance = - ((LinearWorld*)World::getWorld())->getDistanceDownTrackForKart(indx); + LinearWorld *lin_world = dynamic_cast(World::getWorld()); + // Can happen if a non-lap based race mode is used with a scene file that + // has check defined. + if(!lin_world) + return false; + float current_distance = lin_world->getDistanceDownTrackForKart(indx); bool result =(m_previous_distance[indx]>0.95f*track_length && current_distance<7.0f); if(UserConfigParams::m_check_debug && result) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index a314a368e..f03baf38f 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -47,6 +47,7 @@ using namespace irr; #include "items/item.hpp" #include "items/item_manager.hpp" #include "modes/linear_world.hpp" +#include "modes/easter_egg_hunt.hpp" #include "modes/world.hpp" #include "physics/physical_object.hpp" #include "physics/physics.hpp" @@ -92,6 +93,7 @@ Track::Track(const std::string &filename) m_enable_push_back = true; m_reverse_available = false; m_is_arena = false; + m_has_easter_eggs = false; m_is_cutscene = false; m_camera_far = 1000.0f; m_mini_map = NULL; @@ -354,6 +356,9 @@ void Track::loadTrackInfo() m_screenshot = m_root+"/"+m_screenshot; delete root; + std::string dir = StringUtils::getPath(m_filename); + std::string easter_name = dir+"/easter_eggs.xml"; + m_has_easter_eggs = file_manager->fileExists(easter_name); } // loadTrackInfo //----------------------------------------------------------------------------- @@ -1238,9 +1243,7 @@ void Track::createWater(const XMLNode &node) * \param mode_id Which of the modes of a track to use. This determines which * scene, quad, and graph file to load. */ - -void Track::loadTrackModel(World* parent, bool reverse_track, - unsigned int mode_id ) +void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) { if(!m_reverse_available) { @@ -1390,7 +1393,8 @@ void Track::loadTrackModel(World* parent, bool reverse_track, createWater(*node); } else if(name=="banana" || name=="item" || - name=="small-nitro" || name=="big-nitro") + name=="small-nitro" || name=="big-nitro" || + name=="easter-egg" ) { // will be handled later } @@ -1420,7 +1424,9 @@ void Track::loadTrackModel(World* parent, bool reverse_track, } else if(name=="checks") { - CheckManager::get()->load(*node); + // Easter egg hunts don't have laps. + if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_EASTER_EGG) + CheckManager::get()->load(*node); } else if (name=="particle-emitter") { @@ -1543,7 +1549,7 @@ void Track::loadTrackModel(World* parent, bool reverse_track, } //} m_track_object_manager->enableFog(m_use_fog); - + // Sky dome and boxes support // -------------------------- if(m_sky_type==SKY_DOME && m_sky_textures.size() > 0) @@ -1573,8 +1579,8 @@ void Track::loadTrackModel(World* parent, bool reverse_track, } else if(m_sky_type==SKY_COLOR) { - parent->setClearBackBuffer(true); - parent->setClearbackBufferColor(m_sky_color); + World::getWorld()->setClearBackBuffer(true); + World::getWorld()->setClearbackBufferColor(m_sky_color); } @@ -1609,25 +1615,12 @@ void Track::loadTrackModel(World* parent, bool reverse_track, for(unsigned int i=0; igetNumNodes(); i++) { const XMLNode *node = root->getNode(i); - const std::string name = node->getName(); - if (name=="banana" || name=="item" || - name=="small-nitro" || name=="big-nitro") + const std::string &name = node->getName(); + if (name=="banana" || name=="item" || + name=="small-nitro" || name=="big-nitro" || + name=="easter-egg" ) { - Item::ItemType type; - if (name=="banana" ) type = Item::ITEM_BANANA; - else if(name=="item" ) type = Item::ITEM_BONUS_BOX; - else if(name=="small-nitro") type = Item::ITEM_NITRO_SMALL; - else type = Item::ITEM_NITRO_BIG; - Vec3 xyz; - // Set some kind of default in case Z is not defined in the file - // (with the new track exporter it always is defined anyway). - // Z is the height from which the item is dropped on the track. - xyz.setY(1000); - node->getXYZ(&xyz); - bool drop=true; - node->get("drop", &drop); - // Height is needed if bit 2 (for z) is not set - itemCommand(xyz, type, drop); + itemCommand(node); } } // for igetNumNodes() @@ -1645,7 +1638,8 @@ void Track::loadTrackModel(World* parent, bool reverse_track, { printf("WARNING: no check lines found in track '%s'.\n", m_ident.c_str()); - printf("Lap counting will not work, and start positions might be incorrect.\n"); + printf("Lap counting will not work, and start positions might be " + "incorrect.\n"); } if(UserConfigParams::logMemory()) @@ -1654,10 +1648,18 @@ void Track::loadTrackModel(World* parent, bool reverse_track, irr_driver->getSceneManager()->getMeshCache()->getMeshCount(), irr_driver->getVideoDriver()->getTextureCount()); - if (World::getWorld()->useChecklineRequirements()) + World *world = World::getWorld(); + if (world->useChecklineRequirements()) { QuadGraph::get()->computeChecklineRequirements(); } + + EasterEggHunt *easter_world = dynamic_cast(world); + if(easter_world) + { + std::string dir = StringUtils::getPath(m_filename); + easter_world->readData(dir+"/easter_eggs.xml"); + } } // loadTrackModel //----------------------------------------------------------------------------- @@ -1804,14 +1806,38 @@ void Track::handleSky(const XMLNode &xml_node, const std::string &filename) * \param drop True if the item Z position should be determined based on * the track topology. */ -void Track::itemCommand(const Vec3 &xyz, Item::ItemType type, - bool drop) +void Track::itemCommand(const XMLNode *node) { + const std::string &name = node->getName(); + + Item::ItemType type; + if (name=="banana" ) type = Item::ITEM_BANANA; + else if(name=="item" ) type = Item::ITEM_BONUS_BOX; + else if(name=="small-nitro") type = Item::ITEM_NITRO_SMALL; + else if(name=="easter-egg" ) type = Item::ITEM_EASTER_EGG; + else type = Item::ITEM_NITRO_BIG; + Vec3 xyz; + // Set some kind of default in case Y is not defined in the file + // (with the new track exporter it always is defined anyway). + // Y is the height from which the item is dropped on the track. + xyz.setY(1000); + node->getXYZ(&xyz); + bool drop=true; + node->get("drop", &drop); + // Some modes (e.g. time trial) don't have any bonus boxes if(type==Item::ITEM_BONUS_BOX && !World::getWorld()->haveBonusBoxes()) return; + // Only do easter eggs in easter egg mode. + if(type==Item::ITEM_EASTER_EGG && + !(race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG)) + { + printf("Found easter egg in non-easter-egg mode - ignored.\n"); + return; + } + Vec3 loc(xyz); // if only 2d coordinates are given, let the item fall from very high if(drop) diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index a53b8982a..925a63285 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -227,6 +227,9 @@ private: Vec3 m_aabb_max; /** True if this track is an arena. */ bool m_is_arena; + + /** True if this track has easter eggs. */ + bool m_has_easter_eggs; bool m_is_cutscene; @@ -358,8 +361,6 @@ private: std::vector m_all_curves; void loadTrackInfo(); - void itemCommand(const Vec3 &xyz, Item::ItemType item_type, - bool drop); void loadQuadGraph(unsigned int mode_id, const bool reverse); void convertTrackToBullet(scene::ISceneNode *node); bool loadMainTrack(const XMLNode &node); @@ -386,7 +387,9 @@ public: void update(float dt); void reset(); void adjustForFog(scene::ISceneNode *node); - void adjustForFog(scene::IMesh* mesh, scene::ISceneNode* parent_scene_node); + void adjustForFog(scene::IMesh* mesh, + scene::ISceneNode* parent_scene_node); + void itemCommand(const XMLNode *node); const core::vector3df& getSunRotation(); /** Sets the current ambient color for a kart with index k. */ void setAmbientColor(const video::SColor &color, @@ -394,6 +397,8 @@ public: void handleExplosion(const Vec3 &pos, const PhysicalObject *mp, bool secondary_hits=true) const; + void loadTrackModel (bool reverse_track = false, + unsigned int mode_id=0); std::vector< std::vector > buildHeightMap(); // ------------------------------------------------------------------------ @@ -402,11 +407,11 @@ public: // ------------------------------------------------------------------------ const core::dimension2du& getMiniMapSize() const { return m_mini_map_size; } // ------------------------------------------------------------------------ - bool isArena () const { return m_is_arena; } + /** Returns true if this track has an arena mode. */ + bool isArena() const { return m_is_arena; } // ------------------------------------------------------------------------ - void loadTrackModel (World* parent, - bool reverse_track = false, - unsigned int mode_id=0); + /** Returns true if this track has easter eggs. */ + bool hasEasterEggs() const { return m_has_easter_eggs; } // ------------------------------------------------------------------------ void addMusic (MusicInformation* mi) {m_music.push_back(mi); }