diff --git a/.travis.yml b/.travis.yml
index 5cdbe2ecc..ae4e22d25 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,17 +21,21 @@ addons:
apt:
packages:
- build-essential
- - libogg-dev
- - libvorbis-dev
- - libopenal-dev
- - libxxf86vm-dev
- - libcurl4-openssl-dev
- - libfribidi-dev
- - libbluetooth-dev
- - libgl1-mesa-dev
- - libglu1-mesa-dev
- - libglew-dev
- cmake
+ - libbluetooth-dev
+ - libcurl4-gnutls-dev
+ - libfreetype6-dev
+ - libfribidi-dev
+ - libgl1-mesa-dev
+ - libjpeg-dev
+ - libogg-dev
+ - libopenal-dev
+ - libpng-dev
+ - libvorbis-dev
+ - libxrandr-dev
+ - mesa-common-dev
+ - pkg-config
+ - zlib1g-dev
before_script:
- export THREADS=$((`nproc` + 1))
diff --git a/data/supertuxkart.appdata.xml b/data/supertuxkart.appdata.xml
index bed5375cb..412961b51 100644
--- a/data/supertuxkart.appdata.xml
+++ b/data/supertuxkart.appdata.xml
@@ -25,7 +25,7 @@
against the computer or your friends, and more!
- http://supertuxkart.sourceforge.net/
+ http://supertuxkart.net/
http://supertuxkart.sourceforge.net/persistent/images/4/4e/Supertuxkart-0.9-screenshot-2.jpg
http://supertuxkart.sourceforge.net/persistent/images/a/a9/Supertuxkart-0.9-screenshot-1.jpg
diff --git a/doc/conventions.txt b/doc/conventions.txt
index 1926e07d7..11f79b04d 100644
--- a/doc/conventions.txt
+++ b/doc/conventions.txt
@@ -12,7 +12,7 @@ Coding style
============
The coding style used in Super Tux Kart can be found at
-http://supertuxkart.sourceforge.net/Coding_Style
+http://supertuxkart.net/Coding_Style
Documentation line length
diff --git a/src/font/font_manager.cpp b/src/font/font_manager.cpp
index 772c7de22..df237fb0b 100644
--- a/src/font/font_manager.cpp
+++ b/src/font/font_manager.cpp
@@ -52,17 +52,21 @@ void FontManager::loadFonts()
m_digit_ttf = new FaceTTF(stk_config->m_digit_ttf);
// Now load fonts with settings of ttf file
+ unsigned int font_loaded = 0;
RegularFace* regular = new RegularFace(m_normal_ttf);
regular->init();
m_fonts.push_back(regular);
+ m_font_type_map[std::type_index(typeid(RegularFace))] = font_loaded++;
BoldFace* bold = new BoldFace(m_normal_ttf);
bold->init();
m_fonts.push_back(bold);
+ m_font_type_map[std::type_index(typeid(BoldFace))] = font_loaded++;
DigitFace* digit = new DigitFace(m_digit_ttf);
digit->init();
m_fonts.push_back(digit);
+ m_font_type_map[std::type_index(typeid(DigitFace))] = font_loaded++;
} // loadFonts
// ----------------------------------------------------------------------------
diff --git a/src/font/font_manager.hpp b/src/font/font_manager.hpp
index 68f2118cc..aae90c0f3 100644
--- a/src/font/font_manager.hpp
+++ b/src/font/font_manager.hpp
@@ -25,6 +25,8 @@
#include "utils/ptr_vector.hpp"
#include
+#include
+#include
#include
#include FT_FREETYPE_H
@@ -35,13 +37,15 @@ class FontWithFace;
class FontManager : public NoCopy
{
private:
- PtrVector m_fonts;
+ PtrVector m_fonts;
- FT_Library m_ft_library;
+ FT_Library m_ft_library;
- FaceTTF* m_normal_ttf;
+ FaceTTF* m_normal_ttf;
- FaceTTF* m_digit_ttf;
+ FaceTTF* m_digit_ttf;
+
+ std::unordered_map m_font_type_map;
public:
LEAK_CHECK()
@@ -53,11 +57,11 @@ public:
template T* getFont()
{
T* out = NULL;
- for (unsigned int i = 0; i < m_fonts.size(); i++)
+ const unsigned int n = m_font_type_map[std::type_index(typeid(T))];
+ out = dynamic_cast(m_fonts.get(n));
+ if (out != NULL)
{
- out = dynamic_cast(m_fonts.get(i));
- if (out != NULL)
- return out;
+ return out;
}
Log::fatal("FontManager", "Can't get a font!");
return out;
diff --git a/src/font/font_with_face.cpp b/src/font/font_with_face.cpp
index 579169131..0e1659ae0 100644
--- a/src/font/font_with_face.cpp
+++ b/src/font/font_with_face.cpp
@@ -26,7 +26,7 @@
#include "guiengine/skin.hpp"
#include "utils/string_utils.hpp"
-#include
+#include FT_OUTLINE_H
// ----------------------------------------------------------------------------
FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf)
@@ -145,10 +145,11 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index,
FT_LOAD_DEFAULT), "loading a glyph");
- if (dynamic_cast(this))
+ if (dynamic_cast(this) != NULL)
{
// Embolden the outline of the glyph
- FT_Outline_Embolden(&(slot->outline), getDPI() * 2);
+ font_manager->checkFTError(FT_Outline_Embolden(&(slot->outline),
+ getDPI() * 2), "embolden a glyph");
}
font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL),
@@ -321,7 +322,7 @@ void FontWithFace::dumpGlyphPage()
// ----------------------------------------------------------------------------
void FontWithFace::setDPI()
{
- // Get face dpi:
+ // Set face dpi:
// Font size is resolution-dependent.
// Normal text will range from 0.8, in 640x* resolutions (won't scale
// below that) to 1.0, in 1024x* resolutions, and linearly up
@@ -445,7 +446,7 @@ void FontWithFace::render(const core::stringw& text,
FontSettings* font_settings,
FontCharCollector* char_collector)
{
- const bool is_bold_face = dynamic_cast(this);
+ const bool is_bold_face = (dynamic_cast(this) != NULL);
const bool black_border = font_settings ?
font_settings->useBlackBorder() : false;
const bool rtl = font_settings ? font_settings->isRTL() : false;
@@ -513,7 +514,7 @@ void FontWithFace::render(const core::stringw& text,
if (c==L'\r' && text[i+1]==L'\n')
c = text[++i];
offset.Y += m_font_max_height * scale;
- offset.X = position.UpperLeftCorner.X;
+ offset.X = float(position.UpperLeftCorner.X);
if (hcenter)
offset.X += (position.getWidth() - text_dimension.Width) >> 1;
continue;
diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp
index 8966fb51a..fc2c37379 100644
--- a/src/graphics/material.cpp
+++ b/src/graphics/material.cpp
@@ -19,8 +19,9 @@
#include "graphics/material.hpp"
-#include
+#include
#include
+#include
#include "audio/sfx_base.hpp"
#include "audio/sfx_buffer.hpp"
diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp
index 93ef5e802..930c8c1bb 100644
--- a/src/items/powerup.cpp
+++ b/src/items/powerup.cpp
@@ -407,7 +407,7 @@ void Powerup::hitBonusBox(const Item &item, int add_info)
{
new_powerup = powerup_manager->getRandomPowerup(position, &n);
if(new_powerup != PowerupManager::POWERUP_RUBBERBALL ||
- ( World::getWorld()->getTime() - powerup_manager->getBallCollectTime()) >
+ ( World::getWorld()->getTimeSinceStart() - powerup_manager->getBallCollectTime()) >
RubberBall::getTimeBetweenRubberBalls() )
break;
}
diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp
index 085addbaa..ce8307808 100644
--- a/src/karts/controller/ai_base_controller.cpp
+++ b/src/karts/controller/ai_base_controller.cpp
@@ -241,7 +241,7 @@ void AIBaseController::crashed(const Material *m)
const unsigned int NUM_COLLISION = 3;
const float COLLISION_TIME = 1.5f;
- float time = World::getWorld()->getTime();
+ float time = World::getWorld()->getTimeSinceStart();
if(m_collision_times.size()==0)
{
m_collision_times.push_back(time);
diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp
index 633a54da6..802a9e19c 100644
--- a/src/karts/kart.cpp
+++ b/src/karts/kart.cpp
@@ -1001,7 +1001,7 @@ void Kart::collectedItem(Item *item, int add_info)
*/
float Kart::getStartupBoost() const
{
- float t = World::getWorld()->getTime();
+ float t = World::getWorld()->getTimeSinceStart();
std::vector startup_times = m_kart_properties->getStartupTime();
for (unsigned int i = 0; i < startup_times.size(); i++)
{
@@ -1991,9 +1991,9 @@ void Kart::crashed(const Material *m, const Vec3 &normal)
*/
void Kart::playCrashSFX(const Material* m, AbstractKart *k)
{
- if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return;
+ if(World::getWorld()->getTimeSinceStart()-m_time_last_crash < 0.5f) return;
- m_time_last_crash = World::getWorld()->getTime();
+ m_time_last_crash = World::getWorld()->getTimeSinceStart();
// After a collision disable the engine for a short time so that karts
// can 'bounce back' a bit (without this the engine force will prevent
// karts from bouncing back, they will instead stuck towards the obstable).
diff --git a/src/main.cpp b/src/main.cpp
index d8958f1c2..39efb33b1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -194,6 +194,7 @@
#include "states_screens/state_manager.hpp"
#include "states_screens/user_screen.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
+#include "tracks/battle_graph.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/command_line.hpp"
@@ -591,7 +592,7 @@ void cmdLineHelp()
" --shadows=n Set shadow quality (0 to disable shadows).\n"
"\n"
"You can visit SuperTuxKart's homepage at "
- "http://supertuxkart.sourceforge.net\n\n",
+ "http://supertuxkart.net\n\n",
CommandLine::getExecName().c_str()
);
} // cmdLineHelp
@@ -1843,12 +1844,12 @@ void runUnitTests()
{
Log::info("UnitTest", "Starting unit testing");
Log::info("UnitTest", "=====================");
- Log::info("UnitTest", "- GraphicsRestrictions");
+ Log::info("UnitTest", "GraphicsRestrictions");
GraphicsRestrictions::unitTesting();
- Log::info("UnitTest", " - NetworkString");
+ Log::info("UnitTest", "NetworkString");
NetworkString::unitTesting();
- Log::info("UnitTest", " - Easter detection");
+ Log::info("UnitTest", "Easter detection");
// Test easter mode: in 2015 Easter is 5th of April - check with 0 days
// before and after
int saved_easter_mode = UserConfigParams::m_easter_ear_mode;
@@ -1880,9 +1881,12 @@ void runUnitTests()
UserConfigParams::m_easter_ear_mode = saved_easter_mode;
- Log::info("UnitTest", " - Kart characteristics");
+ Log::info("UnitTest", "Kart characteristics");
CombinedCharacteristic::unitTesting();
+ Log::info("UnitTest", "Battle Graph");
+ BattleGraph::unitTesting();
+
Log::info("UnitTest", "=====================");
Log::info("UnitTest", "Testing successful ");
Log::info("UnitTest", "=====================");
diff --git a/src/modes/follow_the_leader.cpp b/src/modes/follow_the_leader.cpp
index 7b29a8653..626d1260a 100644
--- a/src/modes/follow_the_leader.cpp
+++ b/src/modes/follow_the_leader.cpp
@@ -113,15 +113,6 @@ const btTransform &FollowTheLeaderRace::getStartTransform(int index)
return m_track->getStartTransform(start_index);
} // getStartTransform
-//-----------------------------------------------------------------------------
-/** Returns the original time at which the countdown timer started. This is
- * used by the race_gui to display the music credits in FTL mode correctly.
- */
-float FollowTheLeaderRace::getClockStartTime() const
-{
- return m_leader_intervals[0];
-} // getClockStartTime
-
//-----------------------------------------------------------------------------
/** Called when a kart must be eliminated.
*/
diff --git a/src/modes/follow_the_leader.hpp b/src/modes/follow_the_leader.hpp
index 37e395b8f..dcfaa62be 100644
--- a/src/modes/follow_the_leader.hpp
+++ b/src/modes/follow_the_leader.hpp
@@ -49,7 +49,6 @@ public:
virtual void reset() OVERRIDE;
virtual const std::string& getIdent() const OVERRIDE;
virtual const btTransform &getStartTransform(int index) OVERRIDE;
- virtual float getClockStartTime() const;
virtual void getKartsDisplayInfo(
std::vector *info) OVERRIDE;
virtual void init() OVERRIDE;
diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp
index 0879e475e..381500eba 100644
--- a/src/modes/soccer_world.cpp
+++ b/src/modes/soccer_world.cpp
@@ -34,6 +34,7 @@
#include "karts/controller/soccer_ai.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
+#include "tracks/battle_graph.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "utils/constants.hpp"
diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp
index c770d82a0..83282c045 100644
--- a/src/modes/three_strikes_battle.cpp
+++ b/src/modes/three_strikes_battle.cpp
@@ -17,9 +17,6 @@
#include "modes/three_strikes_battle.hpp"
-#include
-#include
-
#include "main_loop.hpp"
#include "audio/music_manager.hpp"
#include "config/user_config.hpp"
@@ -31,10 +28,14 @@
#include "karts/kart_properties.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
+#include "tracks/battle_graph.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "utils/constants.hpp"
+#include
+#include
+
//-----------------------------------------------------------------------------
/** Constructor. Sets up the clock mode etc.
*/
diff --git a/src/modes/world.cpp b/src/modes/world.cpp
index 2cbd03982..413c8d3c5 100644
--- a/src/modes/world.cpp
+++ b/src/modes/world.cpp
@@ -461,6 +461,7 @@ World::~World()
race_manager->setRaceGhostKarts(false);
race_manager->setRecordRace(false);
race_manager->setWatchingReplay(false);
+ race_manager->setTimeTarget(0.0f);
Camera::removeAllCameras();
diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp
index 880a18175..81e066a0e 100644
--- a/src/modes/world_status.cpp
+++ b/src/modes/world_status.cpp
@@ -70,7 +70,8 @@ void WorldStatus::reset()
{
m_time = 0.0f;
m_auxiliary_timer = 0.0f;
-
+ m_count_up_timer = 0.0f;
+
m_engines_started = false;
// Using SETUP_PHASE will play the track into sfx first, and has no
@@ -340,16 +341,19 @@ void WorldStatus::update(const float dt)
{
case CLOCK_CHRONO:
m_time += dt;
+ m_count_up_timer += dt;
break;
case CLOCK_COUNTDOWN:
// stop countdown when race is over
if (m_phase == RESULT_DISPLAY_PHASE || m_phase == FINISH_PHASE)
{
m_time = 0.0f;
+ m_count_up_timer = 0.0f;
break;
}
m_time -= dt;
+ m_count_up_timer += dt;
if(m_time <= 0.0)
{
diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp
index 736dcfb6b..0a329d8f9 100644
--- a/src/modes/world_status.hpp
+++ b/src/modes/world_status.hpp
@@ -114,6 +114,8 @@ private:
*/
float m_auxiliary_timer;
+ float m_count_up_timer;
+
bool m_engines_started;
void startEngines();
public:
@@ -172,6 +174,10 @@ public:
/** Called when the race actually starts. */
virtual void onGo() {};
+ // ------------------------------------------------------------------------
+ /** Get the time since start regardless of which way the clock counts */
+ float getTimeSinceStart() const { return m_count_up_timer; }
+
}; // WorldStatus
diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp
index 5d69851e8..03606a355 100644
--- a/src/states_screens/race_gui_base.cpp
+++ b/src/states_screens/race_gui_base.cpp
@@ -41,7 +41,7 @@
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "karts/rescue_animation.hpp"
-#include "modes/follow_the_leader.hpp"
+#include "modes/linear_world.hpp"
#include "modes/world.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
@@ -457,15 +457,7 @@ void RaceGUIBase::drawGlobalMusicDescription()
gui::IGUIFont* font = GUIEngine::getFont();
- float race_time = World::getWorld()->getTime();
- // In the modes that the clock counts backwards, convert the
- // countdown time to time since start:
- if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
- race_time = ((FollowTheLeaderRace*)World::getWorld())->getClockStartTime()
- - race_time;
- else if (race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER &&
- race_manager->hasTimeTarget())
- race_time = race_manager->getTimeTarget() - World::getWorld()->getTime();
+ float race_time = World::getWorld()->getTimeSinceStart();
// ---- Manage pulsing effect
// 3.0 is the duration of ready/set (TODO: don't hardcode)
diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp
index 232d70321..2f7a9ab8a 100644
--- a/src/tracks/battle_graph.cpp
+++ b/src/tracks/battle_graph.cpp
@@ -23,12 +23,17 @@
#include
#include "config/user_config.hpp"
+#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "items/item_manager.hpp"
#include "race/race_manager.hpp"
#include "tracks/navmesh.hpp"
+#include "tracks/track.hpp"
+#include "tracks/track_manager.hpp"
#include "utils/log.hpp"
+#include
+
const int BattleGraph::UNKNOWN_POLY = -1;
BattleGraph * BattleGraph::m_battle_graph = NULL;
@@ -36,15 +41,19 @@ BattleGraph * BattleGraph::m_battle_graph = NULL;
* then runs shortest path algorithm to find and store paths to be used
* by the AI. */
BattleGraph::BattleGraph(const std::string &navmesh_file_name,
- const XMLNode& node)
+ const XMLNode *node)
{
m_items_on_graph.clear();
NavMesh::create(navmesh_file_name);
m_navmesh_file = navmesh_file_name;
buildGraph(NavMesh::get());
- computeFloydWarshall();
- if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
+
+ // Compute shortest distance from all nodes
+ for(unsigned int i=0; i < NavMesh::get()->getNumberOfPolys(); i++)
+ computeDijkstra(i);
+
+ if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
loadGoalNodes(node);
} // BattleGraph
@@ -59,21 +68,23 @@ BattleGraph::~BattleGraph(void)
cleanupDebugMesh();
} // ~BattleGraph
-// -----------------------------------------------------------------------------
-/** Builds a graph from an existing NavMesh. The graph is stored as an adjacency
-* matrix. */
+// ----------------------------------------------------------------------------
+/** Builds a graph from an existing NavMesh. The graph is stored as an
+ * adjacency matrix. */
void BattleGraph::buildGraph(NavMesh* navmesh)
{
unsigned int n_polys = navmesh->getNumberOfPolys();
- m_distance_matrix = std::vector< std::vector > (n_polys, std::vector(n_polys, 9999.9f));
+ m_distance_matrix = std::vector< std::vector >
+ (n_polys, std::vector(n_polys, 9999.9f));
for(unsigned int i=0; igetNavPoly(i);
- std::vector adjacents = navmesh->getAdjacentPolys(i);
+ const std::vector &adjacents = navmesh->getAdjacentPolys(i);
for(unsigned int j=0; jgetCenterOfPoly(adjacents[j]) - currentPoly.getCenter();
+ Vec3 diff = navmesh->getCenterOfPoly(adjacents[j])
+ - currentPoly.getCenter();
float distance = diff.length();
m_distance_matrix[i][adjacents[j]] = distance;
//m_distance_matrix[adjacents[j]][i] = distance;
@@ -81,14 +92,88 @@ void BattleGraph::buildGraph(NavMesh* navmesh)
m_distance_matrix[i][i] = 0.0f;
}
+ // Allocate and initialise the previous node data structure:
+ unsigned int n = getNumNodes();
+ m_parent_poly = std::vector< std::vector >
+ (n, std::vector(n, BattleGraph::UNKNOWN_POLY));
+ for(unsigned int i=0; i=9899.9f)
+ m_parent_poly[i][j]=-1;
+ else
+ m_parent_poly[i][j] = i;
+ } // for j
+ } // for i
+
} // buildGraph
-// -----------------------------------------------------------------------------
-/** computeFloydWarshall() computes the shortest distance between any two nodes.
- * At the end of the computation, m_distance_matrix[i][j] stores the shortest path
- * distance from i to j and m_parent_poly[i][j] stores the last vertex visited on the
- * shortest path from i to j before visiting j. Suppose the shortest path from i to j is
- * i->......->k->j then m_parent_poly[i][j] = k
+// ----------------------------------------------------------------------------
+/** Dijkstra shortest path computation. It computes the shortest distance from
+ * the specified node 'source' to all other nodes. At the end of the
+ * computation, m_distance_matrix[i][j] stores the shortest path distance from
+ * source to j and m_parent_poly[source][j] stores the last vertex visited on
+ * the shortest path from i to j before visiting j. Suppose the shortest path
+ * from i to j is i->......->k->j then m_parent_poly[i][j] = k
+ */
+void BattleGraph::computeDijkstra(int source)
+{
+ // Stores the distance (float) to 'source' from a specified node (int)
+ typedef std::pair IndDistPair;
+
+ class Shortest
+ {
+ public:
+ bool operator()(const IndDistPair &p1, const IndDistPair &p2)
+ {
+ return p1.second > p2.second;
+ }
+ };
+ std::priority_queue, Shortest> queue;
+ IndDistPair begin(source, 0.0f);
+ queue.push(begin);
+ const unsigned int n=getNumNodes();
+ std::vector visited;
+ visited.resize(n, false);
+ NavMesh *navmesh = NavMesh::get();
+ while(!queue.empty())
+ {
+ // Get element with shortest path
+ IndDistPair current = queue.top();
+ queue.pop();
+ int cur_index = current.first;
+ if(visited[cur_index]) continue;
+ visited[cur_index] = true;
+ const NavPoly ¤t_poly = navmesh->getNavPoly(cur_index);
+ const std::vector &adjacents = current_poly.getAdjacents();
+ for(unsigned int j=0; j......->k->j then
+ * m_parent_poly[i][j] = k
*/
void BattleGraph::computeFloydWarshall()
{
@@ -244,12 +329,12 @@ const bool BattleGraph::differentNodeColor(int n, NodeColor* c) const
} // differentNodeColor
// -----------------------------------------------------------------------------
-void BattleGraph::loadGoalNodes(const XMLNode& node)
+void BattleGraph::loadGoalNodes(const XMLNode *node)
{
m_red_node.clear();
m_blue_node.clear();
- const XMLNode *check_node = node.getNode("checks");
+ const XMLNode *check_node = node->getNode("checks");
for (unsigned int i = 0; i < check_node->getNumNodes(); i++)
{
const XMLNode *goal = check_node->getNode(i);
@@ -277,3 +362,97 @@ void BattleGraph::loadGoalNodes(const XMLNode& node)
}
}
} // loadGoalNodes
+
+// ============================================================================
+/** Unit testing for battle graph distance and parent node computation.
+ * Instead of using hand-tuned test cases we use the tested, verified and
+ * easier to understand Floyd-Warshall algorithm to compute the distances,
+ * and check if the (significanty faster) Dijkstra algorithm gives the same
+ * results. For now we use the cave mesh as test case.
+ */
+void BattleGraph::unitTesting()
+{
+ Track *track = track_manager->getTrack("cave");
+ std::string navmesh_file_name=track->getTrackFile("navmesh.xml");
+
+ double s = StkTime::getRealTime();
+ BattleGraph *bg = new BattleGraph(navmesh_file_name);
+ double e = StkTime::getRealTime();
+ Log::error("Time", "Dijkstra %lf", e-s);
+
+ // Save the Dijkstra results
+ std::vector< std::vector< float > > distance_matrix = bg->m_distance_matrix;
+ std::vector< std::vector< int > > parent_poly = bg->m_parent_poly;
+ bg->buildGraph(NavMesh::get());
+
+ // Now compute results with Floyd-Warshall
+ s = StkTime::getRealTime();
+ bg->computeFloydWarshall();
+ e = StkTime::getRealTime();
+ Log::error("Time", "Floyd-Warshall %lf", e-s);
+
+ int error_count = 0;
+ for(unsigned int i=0; im_distance_matrix.size(); i++)
+ {
+ for(unsigned int j=0; jm_distance_matrix[i].size(); j++)
+ {
+ if(bg->m_distance_matrix[i][j] - distance_matrix[i][j] > 0.001f)
+ {
+ Log::error("BattleGraph",
+ "Incorrect distance %d, %d: Dijkstra: %f F.W.: %f",
+ i, j, distance_matrix[i][j], bg->m_distance_matrix[i][j]);
+ error_count++;
+ } // if distance is too different
+
+ // Unortunately it happens frequently that there are different
+ // shortest path with the same length. And Dijkstra might find
+ // a different path then Floyd-Warshall. So the test for parent
+ // polygon often results in false positives, so it is disabled,
+ // but I leave the code in place in case it is useful for some
+ // debugging in the feature
+#undef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
+#ifdef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
+ if(bg->m_parent_poly[i][j] != parent_poly[i][j])
+ {
+ error_count++;
+ std::vector dijkstra_path = getPathFromTo(i, j, parent_poly);
+ std::vector floyd_path = getPathFromTo(i, j, bg->m_parent_poly);
+ if(dijkstra_path.size()!=floyd_path.size())
+ {
+ Log::error("BattleGraph",
+ "Incorrect path length %d, %d: Dijkstra: %d F.W.: %d",
+ i, j, parent_poly[i][j], bg->m_parent_poly[i][j]);
+ continue;
+ }
+ Log::error("BattleGraph", "Path problems from %d to %d:",
+ i, j);
+ for (unsigned k = 0; k < dijkstra_path.size(); k++)
+ {
+ if(dijkstra_path[k]!=floyd_path[k])
+ Log::error("BattleGraph", "%d/%d dijkstra: %d floyd %d",
+ k, dijkstra_path.size(), dijkstra_path[k],
+ floyd_path[k]);
+ } // for k BattleGraph::getPathFromTo(int from, int to,
+ const std::vector< std::vector< int > > parent_poly)
+{
+ std::vector path;
+ path.push_back(to);
+ while(from!=to)
+ {
+ to = parent_poly[from][to];
+ path.push_back(to);
+ }
+ return path;
+} // getPathFromTo
\ No newline at end of file
diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp
index 03774e49b..da62e7c4e 100644
--- a/src/tracks/battle_graph.hpp
+++ b/src/tracks/battle_graph.hpp
@@ -63,9 +63,9 @@ private:
void buildGraph(NavMesh*);
void computeFloydWarshall();
- void loadGoalNodes(const XMLNode& node);
+ void loadGoalNodes(const XMLNode *node);
- BattleGraph(const std::string &navmesh_file_name, const XMLNode& node);
+ BattleGraph(const std::string &navmesh_file_name, const XMLNode *node=NULL);
~BattleGraph(void);
// ------------------------------------------------------------------------
@@ -86,17 +86,27 @@ private:
{ return false; }
// ------------------------------------------------------------------------
virtual const bool differentNodeColor(int n, NodeColor* c) const;
+ void computeDijkstra(int n);
+ static std::vector getPathFromTo(int from, int to,
+ const std::vector< std::vector< int > > parent_poly);
public:
static const int UNKNOWN_POLY;
+ void findItemsOnGraphNodes();
+ int pointToNode(const int cur_node,
+ const Vec3& cur_point,
+ bool ignore_vertical) const;
+ static void unitTesting();
+
+
/** Returns the one instance of this object. */
static BattleGraph *get() { return m_battle_graph; }
// ----------------------------------------------------------------------
/** Asserts that no BattleGraph instance exists. Then
* creates a BattleGraph instance. */
static void create(const std::string &navmesh_file_name,
- const XMLNode& node)
+ const XMLNode *node)
{
assert(m_battle_graph==NULL);
m_battle_graph = new BattleGraph(navmesh_file_name, node);
@@ -149,11 +159,6 @@ public:
void insertItems(Item* item, int polygon)
{ m_items_on_graph.push_back(std::make_pair(item, polygon)); }
// ------------------------------------------------------------------------
- void findItemsOnGraphNodes();
- // ------------------------------------------------------------------------
- int pointToNode(const int cur_node,
- const Vec3& cur_point,
- bool ignore_vertical) const;
}; //BattleGraph
#endif
diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp
index 426f777dd..f50bb5c93 100644
--- a/src/tracks/track.cpp
+++ b/src/tracks/track.cpp
@@ -660,7 +660,7 @@ void Track::startMusic() const
*/
void Track::loadBattleGraph(const XMLNode &node)
{
- BattleGraph::create(m_root+"navmesh.xml", node);
+ BattleGraph::create(m_root+"navmesh.xml", &node);
if(BattleGraph::get()->getNumNodes()==0)
{
diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp
index 344806358..87bebcf0c 100644
--- a/src/tracks/track.hpp
+++ b/src/tracks/track.hpp
@@ -43,7 +43,6 @@ class ModelDefinitionLoader;
#include "items/item.hpp"
#include "scriptengine/script_engine.hpp"
#include "tracks/quad_graph.hpp"
-#include "tracks/battle_graph.hpp"
#include "utils/aligned_array.hpp"
#include "utils/translation.hpp"
#include "utils/vec3.hpp"
diff --git a/tools/build-linux-travis.sh b/tools/build-linux-travis.sh
index 94d1ee305..741eb636e 100755
--- a/tools/build-linux-travis.sh
+++ b/tools/build-linux-travis.sh
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Automate the build process on Linux based on
-# http://supertuxkart.sourceforge.net/Build_STK_on_Linux
+# http://supertuxkart.net/Build_STK_on_Linux
# CMake build type
BUILDTYPE=Debug