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); }