From 167f25734c87b0f2c2a0c8af3ffe97b18ea407cd Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 23 Sep 2013 00:13:30 +0000 Subject: [PATCH 002/170] git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14137 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/gui/package-update.png | Bin 13124 -> 0 bytes src/graphics/camera.cpp | 30 +++++++++++++----------- src/modes/world.cpp | 1 - src/states_screens/help_screen_1.cpp | 1 - src/states_screens/main_menu_screen.cpp | 1 - 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/data/gui/package-update.png b/data/gui/package-update.png index 5a8a38eb262c0ed7c22c1ea511ad460be6af6a33..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 13124 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 e6pj%Wal;Qsw>}hCC5^=Z0000getXYZ().toIrrVector(); m_camera->setTarget(xyz); @@ -457,18 +454,24 @@ void Camera::update(float dt) // To view inside tunnels (FIXME 27>15 why??? makes no sense // - the kart should not be visible, but it works) m_camera->setNearValue(27.0); + return; } - else if (m_mode==CM_FINAL) + if(m_mode==CM_FINAL) { handleEndCamera(dt); + return; } + float above_kart, cam_angle, side_way, distance; + bool smoothing; + getCameraSettings(&above_kart, &cam_angle, &side_way, &distance, + &smoothing); + // If an explosion is happening, stop moving the camera, // but keep it target on the kart. - else if (dynamic_cast(m_kart->getKartAnimation())) + if(dynamic_cast(m_kart->getKartAnimation())) { - getCameraSettings(&above_kart, &cam_angle, &side_way, &distance, &smoothing); // The camera target needs to be 'smooth moved', otherwise // there will be a noticable jump in the first frame @@ -483,17 +486,16 @@ void Camera::update(float dt) m_camera->setTarget(current_target); } - else - { - getCameraSettings(&above_kart, &cam_angle, &side_way, &distance, &smoothing); positionCamera(dt, above_kart, cam_angle, side_way, distance, smoothing); - } - if (UserConfigParams::m_graphical_effects && m_rain) + if (UserConfigParams::m_graphical_effects) { - m_rain->setPosition( getCameraSceneNode()->getPosition() ); - m_rain->update(dt); + if (m_rain) + { + m_rain->setPosition( getCameraSceneNode()->getPosition() ); + m_rain->update(dt); + } } // UserConfigParams::m_graphical_effects } // update diff --git a/src/modes/world.cpp b/src/modes/world.cpp index c015f665a..92e76e05f 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -743,7 +743,6 @@ void World::updateWorld(float dt) race_manager->setNumKarts( 1 ); race_manager->setTrack( "tutorial" ); race_manager->setDifficulty(RaceManager::DIFFICULTY_EASY); - race_manager->setReverseTrack(false); // Use keyboard 0 by default (FIXME: let player choose?) InputDevice* device = input_manager->getDeviceList()->getKeyboard(0); diff --git a/src/states_screens/help_screen_1.cpp b/src/states_screens/help_screen_1.cpp index eac26e2ec..20ac321d9 100644 --- a/src/states_screens/help_screen_1.cpp +++ b/src/states_screens/help_screen_1.cpp @@ -59,7 +59,6 @@ void HelpScreen1::eventCallback(Widget* widget, const std::string& name, const i race_manager->setNumKarts( 1 ); race_manager->setTrack( "tutorial" ); race_manager->setDifficulty(RaceManager::DIFFICULTY_EASY); - race_manager->setReverseTrack(false); // Use keyboard 0 by default (FIXME: let player choose?) InputDevice* device = input_manager->getDeviceList()->getKeyboard(0); diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 9169e1000..2bbd7e606 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -308,7 +308,6 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, race_manager->setNumKarts( 1 ); race_manager->setTrack( "tutorial" ); race_manager->setDifficulty(RaceManager::DIFFICULTY_EASY); - race_manager->setReverseTrack(false); // Use keyboard 0 by default (FIXME: let player choose?) InputDevice* device = input_manager->getDeviceList()->getKeyboard(0); From f4fe56dd3123aad0401e93f960632084db3d150d Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 28 Sep 2013 10:20:57 +0000 Subject: [PATCH 003/170] Two new classes for holding the navigation mesh for battle maps. note: proper comments will be added soon git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14166 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/nav_poly.cpp | 69 +++++++++++++++++++++++ src/tracks/nav_poly.hpp | 54 ++++++++++++++++++ src/tracks/navmesh.cpp | 118 ++++++++++++++++++++++++++++++++++++++++ src/tracks/navmesh.hpp | 90 ++++++++++++++++++++++++++++++ 4 files changed, 331 insertions(+) create mode 100644 src/tracks/nav_poly.cpp create mode 100644 src/tracks/nav_poly.hpp create mode 100644 src/tracks/navmesh.cpp create mode 100644 src/tracks/navmesh.hpp diff --git a/src/tracks/nav_poly.cpp b/src/tracks/nav_poly.cpp new file mode 100644 index 000000000..9f3d4db60 --- /dev/null +++ b/src/tracks/nav_poly.cpp @@ -0,0 +1,69 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009 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 "tracks/navmesh.hpp" +#include "tracks/nav_poly.hpp" + + +NavPoly::NavPoly(const std::vector &polygonVertIndices, + const std::vector &adjacentPolygonIndices) +{ + m_vertices = polygonVertIndices; + + m_adjacents = adjacentPolygonIndices; + + std::vector xyz_points = getVertices(); + + Vec3 temp; + for(unsigned int i=0; i NavPoly::getVertices() +{ + std::vector points; + for(unsigned int i=0; igetVertex(m_vertices[i])); + return points; +} + +bool NavPoly::pointInPoly(const Vec3& p) const +{ + std::vector points; + for(unsigned int i=0; igetVertex(m_vertices[i])); + + // The point is on which side of the first edge + float side = p.sideOfLine2D(points[0],points[1]); + + + // The point is inside the polygon if it is on the same side for all edges + for(int i=1; i +#include + +#include "utils/vec3.hpp" + + +class NavPoly +{ +private: + + /** Holds the index of vertices for a polygon **/ + std::vector m_vertices; + + /** Center of this polygon. **/ + Vec3 m_center; + + /** Holds the index of adjacent polyogns **/ + std::vector m_adjacents; + +public: + NavPoly(const std::vector &polygonVertIndices, + const std::vector &adjacentPolygonIndices); + + const Vec3& getCenter() const {return m_center;} + const std::vector& getAdjacents() const {return m_adjacents;} + const std::vector getVertices(); + bool pointInPoly(const Vec3& p) const; +}; + + +#endif \ No newline at end of file diff --git a/src/tracks/navmesh.cpp b/src/tracks/navmesh.cpp new file mode 100644 index 000000000..353e30b11 --- /dev/null +++ b/src/tracks/navmesh.cpp @@ -0,0 +1,118 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009 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 "tracks/navmesh.hpp" +#include "tracks/nav_poly.hpp" + +#include + +#include "io/file_manager.hpp" +#include "io/xml_node.hpp" +#include "utils/string_utils.hpp" + +NavMesh *NavMesh::m_nav_mesh = NULL; + + + +void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const +{ + float x,y,z; + xml->get("x", &x); + xml->get("y", &y); + xml->get("z", &z); + Vec3 temp(x,y,z); + *result = temp; +} + + + +NavMesh::NavMesh(const std::string &filename) +{ + + XMLNode *xml = file_manager->createXMLTree(filename); + if(!xml || xml->getName()!="navmesh") + { + Log::error("NavMesh", "NavMesh '%s' not found. \n", filename.c_str()); + return; + } + + for(unsigned int i=0; igetNumNodes(); i++) + { + const XMLNode *xml_node = xml->getNode(i); + if(xml_node->getName()=="vertices") + { + for(unsigned int i=0; igetNumNodes(); i++) + { + const XMLNode *xml_node_node = xml->getNode(i); + if(xml_node_node->getName()!="vertex") + { + Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n", + xml_node_node->getName().c_str(),filename.c_str()); + continue; + } + + //Reading vertices + Vec3 p; + readVertex(xml_node_node, &p); + m_n_verts++; + m_verts.push_back(p); + } + + } + + if(xml_node->getName()=="faces") + { + for(unsigned int i=0; igetNumNodes(); i++) + { + const XMLNode *xml_node_node = xml->getNode(i); + if(xml_node_node->getName()!="face") + { + Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n", + xml_node_node->getName().c_str(),filename.c_str()); + continue; + } + + //Reading faces/polys + std::vector polygonVertIndices; + std::vector adjacentPolygonIndices; + xml_node_node->get("indices", &polygonVertIndices); + xml_node_node->get("adjacent", &adjacentPolygonIndices); + NavPoly *np = new NavPoly(polygonVertIndices, adjacentPolygonIndices); + m_polys.push_back(*np); + } + + } + + if(xml_node->getName()=="MaxVertsPerPoly") + { + xml_node->get("nvp", &m_nvp); + } + + delete xml_node; + } + + delete xml; + + m_nav_mesh = this; +} + +NavMesh::~NavMesh() +{ + delete m_nav_mesh; + m_nav_mesh = NULL; +} \ No newline at end of file diff --git a/src/tracks/navmesh.hpp b/src/tracks/navmesh.hpp new file mode 100644 index 000000000..240ec817b --- /dev/null +++ b/src/tracks/navmesh.hpp @@ -0,0 +1,90 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009 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, B + +#ifndef HEADER_NAVMESH_GRAPH_HPP +#define HEADER_NAVMESH_GRAPH_HPP + +#include "tracks/nav_poly.hpp" + +#include +#include +#include + +#include "utils/vec3.hpp" + + +class XMLNode; + +class NavMesh +{ + +private: + + static NavMesh *m_nav_mesh; + + std::vector m_polys; + std::vector< Vec3 > m_verts; + unsigned int m_n_verts; + unsigned int m_n_polys; + unsigned int m_nvp; + + void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const; + //void NavMesh::readFace(const XMLNode *xml, Vec3* result) const; + NavMesh(const std::string &filename); + ~NavMesh(); + +public: + + static void create(const std::string &filename) + { + assert(m_nav_mesh==NULL); + m_nav_mesh = new NavMesh(filename); + } + + + static NavMesh *get() { return m_nav_mesh; } + + const NavPoly& getNavPoly(int n) const + { return m_polys[n]; } + + const Vec3& getVertex(int n) const + { return m_verts[n]; } + + unsigned int getNumberOfPolys() const + { return m_n_polys; } + + unsigned int getNumberOfVerts() const + { return m_n_verts; } + + unsigned int getMaxVertsPerPoly() const + { return m_nvp; } + + const Vec3& getCenterOfPoly(int n) const + {return m_polys[n].getCenter();} + + + const std::vector& getAdjacentPolys(int n) const + {return m_polys[n].getAdjacents();} + + + const std::vector getVertsOfPolys(int n) + {return m_polys[n].getVertices();} +}; + + +#endif \ No newline at end of file From c5e1b1a18261d90ccaa3e6a6d386b4afd72b1d43 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 28 Sep 2013 11:13:24 +0000 Subject: [PATCH 004/170] minor update to last commit git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14167 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/ide/vc10/supertuxkart.vcxproj | 6 ++++++ src/ide/vc10/supertuxkart.vcxproj.filters | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/ide/vc10/supertuxkart.vcxproj b/src/ide/vc10/supertuxkart.vcxproj index 1cfd3073b..4b8d59188 100644 --- a/src/ide/vc10/supertuxkart.vcxproj +++ b/src/ide/vc10/supertuxkart.vcxproj @@ -302,9 +302,12 @@ + + + @@ -561,9 +564,12 @@ + + + diff --git a/src/ide/vc10/supertuxkart.vcxproj.filters b/src/ide/vc10/supertuxkart.vcxproj.filters index d07936927..410e2c423 100644 --- a/src/ide/vc10/supertuxkart.vcxproj.filters +++ b/src/ide/vc10/supertuxkart.vcxproj.filters @@ -873,6 +873,15 @@ Source Files\utils + + Source Files\tracks + + + Source Files\tracks + + + Source Files\tracks + @@ -1670,5 +1679,14 @@ Header Files\guiengine\widgets + + Header Files\tracks + + + Source Files\tracks + + + Header Files\tracks + \ No newline at end of file From e0994932bf05ed30be814e3f4bbcd98b48f97468 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 1 Oct 2013 21:30:58 +0000 Subject: [PATCH 005/170] Added preliminary class for holding the battle mode navigation graph. The navigation mesh can now be viewed in game for battleisland ( start with --track-debug --camera-debug ) , the colors are bit messed up though. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14175 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/battle_graph.cpp | 193 ++++++++++++++++++++++++++++++++++++ src/tracks/battle_graph.hpp | 91 +++++++++++++++++ src/tracks/nav_poly.cpp | 18 +++- src/tracks/nav_poly.hpp | 12 ++- src/tracks/navmesh.cpp | 53 ++++++++-- src/tracks/navmesh.hpp | 27 ++++- src/tracks/track.cpp | 21 ++++ src/tracks/track.hpp | 2 + 8 files changed, 401 insertions(+), 16 deletions(-) create mode 100644 src/tracks/battle_graph.cpp create mode 100644 src/tracks/battle_graph.hpp diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp new file mode 100644 index 000000000..10a9660b5 --- /dev/null +++ b/src/tracks/battle_graph.cpp @@ -0,0 +1,193 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009 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, B + +#include "tracks/battle_graph.hpp" + + +#include +#include + +#include "graphics/irr_driver.hpp" +#include "tracks/navmesh.hpp" +#include "utils/vec3.hpp" + + +BattleGraph * BattleGraph::m_battle_graph = NULL; + +BattleGraph::BattleGraph(const std::string &navmesh_file_name) +{ + NavMesh::create(navmesh_file_name); + m_navmesh_file = navmesh_file_name; + buildGraph(NavMesh::get()); +} + +void BattleGraph::buildGraph(NavMesh* navmesh) +{ + unsigned int n_polys = navmesh->getNumberOfPolys(); + m_graph.resize(n_polys); + for(int i=0; igetNavPoly(i); + std::vector adjacents = navmesh->getAdjacentPolys(i); + for(unsigned int j=0; jgetCenterOfPoly(adjacents[j]); + float distance = Vec3(adjacentPolyCenter - currentPoly.getCenter()).length_2d(); + m_graph[i].push_back(std::make_pair(adjacents[i], distance)); + + } + } + +} +BattleGraph::~BattleGraph(void) +{ + +} + +void BattleGraph::createMesh(bool enable_transparency, + const video::SColor *track_color) +{ + // The debug track will not be lighted or culled. + video::SMaterial m; + m.BackfaceCulling = false; + m.Lighting = false; + if(enable_transparency) + m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + m_mesh = irr_driver->createQuadMesh(&m); + m_mesh_buffer = m_mesh->getMeshBuffer(0); + assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD); + + + + // Fetch the number of vertices + unsigned int n = NavMesh::get()->getNumberOfVerts(); + + // Get the reference to a vector of vertices from NavMesh + const std::vector& v = NavMesh::get()->getAllVertices(); + + // Declare array to hold new converted vertices + video::S3DVertex *new_v = new video::S3DVertex[n]; + + // Eps is used to raise the track debug quads a little bit higher than + // the ground, so that they are actually visible. + core::vector3df eps(0, 0.4f, 0); + video::SColor defaultColor(255, 255, 0, 0), c; + + for( unsigned count=0; count ind; + + // Now add all quads + int i=0; + for(unsigned int count=0; countgetNavPoly(count); + + std::vector vInd = poly.getVerticesIndex(); + + // Number of triangles in the triangle fan + unsigned int numberOfTriangles = vInd.size() -2 ; + + // Set up the indices for the triangles + + for( unsigned int count = 1; count<=numberOfTriangles; count++) + { + ind.push_back(vInd[0]); + ind.push_back(vInd[count]); + ind.push_back(vInd[count+1]); + + if(new_v[vInd[0]].Color==defaultColor) new_v[vInd[0]].Color = c; + if(new_v[vInd[count]].Color==defaultColor) new_v[vInd[count]].Color = c; + if(new_v[vInd[count+1]].Color==defaultColor)new_v[vInd[count+1]].Color = c; + + core::triangle3df tri(v[vInd[0]].toIrrVector(), + v[vInd[count]].toIrrVector(), + v[vInd[count+1]].toIrrVector()); + core::vector3df normal = tri.getNormal(); + normal.normalize(); + new_v[vInd[0]].Normal = normal; + new_v[vInd[count]].Normal = normal; + new_v[vInd[count+1]].Normal = normal; + + } + + + //// (note, afaik with opengl we could use quads directly, but the code + //// would not be portable to directx anymore). + //ind[6*i ] = 4*i; // First triangle: vertex 0, 1, 2 + //ind[6*i+1] = 4*i+1; + //ind[6*i+2] = 4*i+2; + //ind[6*i+3] = 4*i; // second triangle: vertex 0, 1, 3 + //ind[6*i+4] = 4*i+2; + //ind[6*i+5] = 4*i+3; + //i++; + } // for i=1; iappend(new_v, n, ind.data(), ind.size()); + + // Instead of setting the bounding boxes, we could just disable culling, + // since the debug track should always be drawn. + //m_node->setAutomaticCulling(scene::EAC_OFF); + m_mesh_buffer->recalculateBoundingBox(); + m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox()); + + delete[] new_v; +} // createMesh + + +/** Creates the debug mesh to display the quad graph on top of the track + * model. */ +void BattleGraph::createDebugMesh() +{ + if(m_graph.size()<=0) return; // no debug output if not graph + + createMesh(/*enable_transparency*/true); + + //// Now colour the quads red/blue/red ... + //video::SColor c( 128, 255, 0, 0); + //video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices(); + //for(unsigned int i=0; igetVertexCount(); i++) + //{ + // // Swap the colours from red to blue and back + // c.setRed ((i%2) ? 255 : 0); + // c.setBlue((i%2) ? 0 : 255); + // v[i].Color = c; + //} + m_node = (scene::ISceneNode*)irr_driver->addMesh(m_mesh); +#ifdef DEBUG + m_node->setName("track-debug-mesh"); +#endif + +} // createDebugMesh diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp new file mode 100644 index 000000000..772b23346 --- /dev/null +++ b/src/tracks/battle_graph.hpp @@ -0,0 +1,91 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009 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, B + +#ifndef HEADER_BATTLE_GRAPH_HPP +#define HEADER_BATTLE_GRAPH_HPP + +#include +#include +#include + +#include "tracks/navmesh.hpp" + + +class Navmesh; + +namespace irr +{ + namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; } + namespace video { class ITexture; } +} +using namespace irr; + +class BattleGraph +{ + +private: + + static BattleGraph *m_battle_graph; + + std::vector< std::vector< std::pair > > m_graph; + + /** For debug mode only: the node of the debug mesh. */ + scene::ISceneNode *m_node; + /** For debug only: the mesh of the debug mesh. */ + scene::IMesh *m_mesh; + /** For debug only: the actual mesh buffer storing the quads. */ + scene::IMeshBuffer *m_mesh_buffer; + + + std::string m_navmesh_file; + + + void buildGraph(NavMesh*); + void createMesh(bool enable_transparency=false, + const video::SColor *track_color=NULL); + + BattleGraph(const std::string &navmesh_file_name); + ~BattleGraph(void); + +public: + + static BattleGraph *get() { return m_battle_graph; } + + static void create(const std::string &navmesh_file_name) + { + assert(m_battle_graph==NULL); + m_battle_graph = new BattleGraph(navmesh_file_name); + + } + + static void destroy() + { + if(m_battle_graph) + { + delete m_battle_graph; + m_battle_graph = NULL; + } + } + + unsigned int getNumNodes() const { return m_graph.size(); } + + void createDebugMesh(); + +}; + +#endif \ No newline at end of file diff --git a/src/tracks/nav_poly.cpp b/src/tracks/nav_poly.cpp index 9f3d4db60..e022b3093 100644 --- a/src/tracks/nav_poly.cpp +++ b/src/tracks/nav_poly.cpp @@ -16,8 +16,12 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "tracks/navmesh.hpp" + + #include "tracks/nav_poly.hpp" +#include "tracks/navmesh.hpp" + +#include NavPoly::NavPoly(const std::vector &polygonVertIndices, @@ -57,7 +61,7 @@ bool NavPoly::pointInPoly(const Vec3& p) const // The point is inside the polygon if it is on the same side for all edges - for(int i=1; igetVertex(m_vertices[i]); +} + diff --git a/src/tracks/nav_poly.hpp b/src/tracks/nav_poly.hpp index b11c3d8f8..6a72ea169 100644 --- a/src/tracks/nav_poly.hpp +++ b/src/tracks/nav_poly.hpp @@ -19,14 +19,14 @@ #ifndef HEADER_NAV_POLY_HPP #define HEADER_NAV_POLY_HPP -#include "tracks/navmesh.hpp" #include #include - +#include #include "utils/vec3.hpp" + class NavPoly { private: @@ -45,9 +45,17 @@ public: const std::vector &adjacentPolygonIndices); const Vec3& getCenter() const {return m_center;} + const std::vector& getAdjacents() const {return m_adjacents;} + const std::vector getVertices(); + + const std::vector getVerticesIndex() const {return m_vertices;} + bool pointInPoly(const Vec3& p) const; + + const Vec3& operator[](int i) const ; + }; diff --git a/src/tracks/navmesh.cpp b/src/tracks/navmesh.cpp index 353e30b11..f634dc18d 100644 --- a/src/tracks/navmesh.cpp +++ b/src/tracks/navmesh.cpp @@ -20,7 +20,11 @@ #include "tracks/nav_poly.hpp" #include +#include +#include + +#include "LinearMath/btTransform.h" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "utils/string_utils.hpp" @@ -44,6 +48,9 @@ void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const NavMesh::NavMesh(const std::string &filename) { + m_n_verts=0; + m_n_polys=0; + XMLNode *xml = file_manager->createXMLTree(filename); if(!xml || xml->getName()!="navmesh") { @@ -51,6 +58,9 @@ NavMesh::NavMesh(const std::string &filename) return; } + // Assigning m_nav_mesh here because constructing NavPoly requires m_nav_mesh to be defined + m_nav_mesh = this; + for(unsigned int i=0; igetNumNodes(); i++) { const XMLNode *xml_node = xml->getNode(i); @@ -58,8 +68,8 @@ NavMesh::NavMesh(const std::string &filename) { for(unsigned int i=0; igetNumNodes(); i++) { - const XMLNode *xml_node_node = xml->getNode(i); - if(xml_node_node->getName()!="vertex") + const XMLNode *xml_node_node = xml_node->getNode(i); + if(!(xml_node_node->getName()=="vertex")) { Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n", xml_node_node->getName().c_str(),filename.c_str()); @@ -79,7 +89,7 @@ NavMesh::NavMesh(const std::string &filename) { for(unsigned int i=0; igetNumNodes(); i++) { - const XMLNode *xml_node_node = xml->getNode(i); + const XMLNode *xml_node_node = xml_node->getNode(i); if(xml_node_node->getName()!="face") { Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n", @@ -94,6 +104,7 @@ NavMesh::NavMesh(const std::string &filename) xml_node_node->get("adjacent", &adjacentPolygonIndices); NavPoly *np = new NavPoly(polygonVertIndices, adjacentPolygonIndices); m_polys.push_back(*np); + m_n_polys++; } } @@ -103,16 +114,46 @@ NavMesh::NavMesh(const std::string &filename) xml_node->get("nvp", &m_nvp); } - delete xml_node; + //delete xml_node; } delete xml; - m_nav_mesh = this; } NavMesh::~NavMesh() { delete m_nav_mesh; m_nav_mesh = NULL; -} \ No newline at end of file +} +// +//void NavMesh::getS3DVertsOfPoly(int n, video::S3DVertex *v, const video::SColor &color) const +//{ +// // Eps is used to raise the track debug quads a little bit higher than +// // the ground, so that they are actually visible. +// std::vector< Vec3 > verticesVec3 = NavMesh::get()->getVertsOfPoly(n); +// core::vector3df eps(0, 0.1f, 0); +// +// for( unsigned int i=0; i& getAllVertices() const + { return m_verts; } + unsigned int getNumberOfPolys() const { return m_n_polys; } @@ -82,8 +96,11 @@ public: {return m_polys[n].getAdjacents();} - const std::vector getVertsOfPolys(int n) + const std::vector getVertsOfPoly(int n) {return m_polys[n].getVertices();} + + /*void getS3DVertsOfPoly(int n, video::S3DVertex *v, + const video::SColor &color) const;*/ }; diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index e3a82f7ed..eb2cdf0c0 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -57,6 +57,7 @@ using namespace irr; #include "physics/triangle_mesh.hpp" #include "race/race_manager.hpp" #include "tracks/bezier_curve.hpp" +#include "tracks/battle_graph.hpp" #include "tracks/check_manager.hpp" #include "tracks/lod_node_loader.hpp" #include "tracks/track_manager.hpp" @@ -168,6 +169,7 @@ void Track::reset() void Track::cleanup() { QuadGraph::destroy(); + BattleGraph::destroy(); ItemManager::destroy(); ParticleKindManager::get()->cleanUpTrackSpecificGfx(); @@ -506,6 +508,18 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse) } } } // loadQuadGraph + +//----------------------------------------------------------------------------- +/** Loads the polygon graph for battle, i.e. the definition of all polys, and the way + * they are connected to each other. Input file name is hardcoded for now + */ +void Track::loadBattleGraph() +{ + BattleGraph::create(m_root+"navmesh.xml"); +} + + + // ----------------------------------------------------------------------------- void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const { @@ -1416,6 +1430,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) // the information about the size of the texture to render the mini // map to. if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadQuadGraph(mode_id, reverse_track); + if (m_is_arena && !m_is_soccer && !m_is_cutscene) loadBattleGraph(); ItemManager::create(); @@ -1735,6 +1750,12 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) !m_is_cutscene) QuadGraph::get()->createDebugMesh(); + if (UserConfigParams::m_track_debug && + race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES && + !m_is_cutscene) + BattleGraph::get()->createDebugMesh(); + + // Only print warning if not in battle mode, since battle tracks don't have // any quads or check lines. if(CheckManager::get()->getCheckStructureCount()==0 && diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 0d180f3ef..19380069c 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -39,6 +39,7 @@ using namespace irr; #include "graphics/material.hpp" #include "items/item.hpp" #include "tracks/quad_graph.hpp" +#include "tracks/battle_graph.hpp" #include "utils/aligned_array.hpp" #include "utils/translation.hpp" #include "utils/vec3.hpp" @@ -370,6 +371,7 @@ private: void loadTrackInfo(); void loadQuadGraph(unsigned int mode_id, const bool reverse); + void loadBattleGraph(); void convertTrackToBullet(scene::ISceneNode *node); bool loadMainTrack(const XMLNode &node); void createWater(const XMLNode &node); From fa3846e1693b9b10ea3f94fc77a9da43ef7e6931 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 1 Oct 2013 22:00:10 +0000 Subject: [PATCH 006/170] Fixed coloring in debug mesh. Each polygon distinctly visible in funky colors. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14176 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/battle_graph.cpp | 112 ++++++++++++++---------------------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index 10a9660b5..75436ffba 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -71,91 +71,78 @@ void BattleGraph::createMesh(bool enable_transparency, m_mesh = irr_driver->createQuadMesh(&m); m_mesh_buffer = m_mesh->getMeshBuffer(0); assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD); - - - - // Fetch the number of vertices - unsigned int n = NavMesh::get()->getNumberOfVerts(); - - // Get the reference to a vector of vertices from NavMesh - const std::vector& v = NavMesh::get()->getAllVertices(); - - // Declare array to hold new converted vertices - video::S3DVertex *new_v = new video::S3DVertex[n]; + // Eps is used to raise the track debug quads a little bit higher than // the ground, so that they are actually visible. core::vector3df eps(0, 0.4f, 0); video::SColor defaultColor(255, 255, 0, 0), c; - - for( unsigned count=0; count new_v; + + // Declare vector to hold indices std::vector ind; - // Now add all quads + // Now add all polygons int i=0; for(unsigned int count=0; countgetNavPoly(count); - std::vector vInd = poly.getVerticesIndex(); - + //std::vector vInd = poly.getVerticesIndex(); + const std::vector& v = poly.getVertices(); + // Number of triangles in the triangle fan - unsigned int numberOfTriangles = vInd.size() -2 ; + unsigned int numberOfTriangles = v.size() -2 ; // Set up the indices for the triangles for( unsigned int count = 1; count<=numberOfTriangles; count++) { - ind.push_back(vInd[0]); - ind.push_back(vInd[count]); - ind.push_back(vInd[count+1]); + video::S3DVertex v1,v2,v3; + v1.Pos=v[0].toIrrVector() + eps; + v2.Pos=v[count].toIrrVector() + eps; + v3.Pos=v[count+1].toIrrVector() + eps; + + v1.Color = c; + v2.Color = c; + v3.Color = c; - if(new_v[vInd[0]].Color==defaultColor) new_v[vInd[0]].Color = c; - if(new_v[vInd[count]].Color==defaultColor) new_v[vInd[count]].Color = c; - if(new_v[vInd[count+1]].Color==defaultColor)new_v[vInd[count+1]].Color = c; - - core::triangle3df tri(v[vInd[0]].toIrrVector(), - v[vInd[count]].toIrrVector(), - v[vInd[count+1]].toIrrVector()); - core::vector3df normal = tri.getNormal(); - normal.normalize(); - new_v[vInd[0]].Normal = normal; - new_v[vInd[count]].Normal = normal; - new_v[vInd[count+1]].Normal = normal; + core::triangle3df tri(v1.Pos, v2.Pos, v3.Pos); + core::vector3df normal = tri.getNormal(); + normal.normalize(); + v1.Normal = normal; + v2.Normal = normal; + v3.Normal = normal; + + new_v.push_back(v1); + new_v.push_back(v2); + new_v.push_back(v3); + + ind.push_back(i++); + ind.push_back(i++); + ind.push_back(i++); + } - - - //// (note, afaik with opengl we could use quads directly, but the code - //// would not be portable to directx anymore). - //ind[6*i ] = 4*i; // First triangle: vertex 0, 1, 2 - //ind[6*i+1] = 4*i+1; - //ind[6*i+2] = 4*i+2; - //ind[6*i+3] = 4*i; // second triangle: vertex 0, 1, 3 - //ind[6*i+4] = 4*i+2; - //ind[6*i+5] = 4*i+3; - //i++; - } // for i=1; iappend(new_v, n, ind.data(), ind.size()); + m_mesh_buffer->append(new_v.data(), v.size(), ind.data(), ind.size()); // Instead of setting the bounding boxes, we could just disable culling, // since the debug track should always be drawn. @@ -163,28 +150,17 @@ void BattleGraph::createMesh(bool enable_transparency, m_mesh_buffer->recalculateBoundingBox(); m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox()); - delete[] new_v; } // createMesh /** Creates the debug mesh to display the quad graph on top of the track * model. */ + void BattleGraph::createDebugMesh() { if(m_graph.size()<=0) return; // no debug output if not graph createMesh(/*enable_transparency*/true); - - //// Now colour the quads red/blue/red ... - //video::SColor c( 128, 255, 0, 0); - //video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices(); - //for(unsigned int i=0; igetVertexCount(); i++) - //{ - // // Swap the colours from red to blue and back - // c.setRed ((i%2) ? 255 : 0); - // c.setBlue((i%2) ? 0 : 255); - // v[i].Color = c; - //} m_node = (scene::ISceneNode*)irr_driver->addMesh(m_mesh); #ifdef DEBUG m_node->setName("track-debug-mesh"); From 479464ede4c936d4d351522a421a88091afa0ab4 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 1 Oct 2013 22:11:31 +0000 Subject: [PATCH 007/170] fixed minor error and removed a redundant function. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14177 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/battle_graph.cpp | 2 +- src/tracks/navmesh.cpp | 29 ----------------------------- src/tracks/navmesh.hpp | 2 -- 3 files changed, 1 insertion(+), 32 deletions(-) diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index 75436ffba..40850b312 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -142,7 +142,7 @@ void BattleGraph::createMesh(bool enable_transparency, } - m_mesh_buffer->append(new_v.data(), v.size(), ind.data(), ind.size()); + m_mesh_buffer->append(new_v.data(), new_v.size(), ind.data(), ind.size()); // Instead of setting the bounding boxes, we could just disable culling, // since the debug track should always be drawn. diff --git a/src/tracks/navmesh.cpp b/src/tracks/navmesh.cpp index f634dc18d..7d6526c84 100644 --- a/src/tracks/navmesh.cpp +++ b/src/tracks/navmesh.cpp @@ -126,34 +126,5 @@ NavMesh::~NavMesh() delete m_nav_mesh; m_nav_mesh = NULL; } -// -//void NavMesh::getS3DVertsOfPoly(int n, video::S3DVertex *v, const video::SColor &color) const -//{ -// // Eps is used to raise the track debug quads a little bit higher than -// // the ground, so that they are actually visible. -// std::vector< Vec3 > verticesVec3 = NavMesh::get()->getVertsOfPoly(n); -// core::vector3df eps(0, 0.1f, 0); -// -// for( unsigned int i=0; i getVertsOfPoly(int n) {return m_polys[n].getVertices();} - /*void getS3DVertsOfPoly(int n, video::S3DVertex *v, - const video::SColor &color) const;*/ }; From c06e49e37e486e1777cfcfed2a127f444466a267 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Wed, 2 Oct 2013 03:34:33 +0000 Subject: [PATCH 008/170] Updated cmake build script. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14182 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- sources.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sources.cmake b/sources.cmake index d35e62256..9e86dc89d 100644 --- a/sources.cmake +++ b/sources.cmake @@ -211,6 +211,7 @@ src/tinygettext/stk_file_system.cpp src/tinygettext/tgt_log.cpp src/tinygettext/tinygettext.cpp src/tracks/ambient_light_sphere.cpp +src/tracks/battle_graph.cpp src/tracks/bezier_curve.cpp src/tracks/check_cannon.cpp src/tracks/check_goal.cpp @@ -221,6 +222,8 @@ src/tracks/check_sphere.cpp src/tracks/check_structure.cpp src/tracks/graph_node.cpp src/tracks/lod_node_loader.cpp +src/tracks/navmesh.cpp +src/tracks/nav_poly.cpp src/tracks/quad.cpp src/tracks/quad_graph.cpp src/tracks/quad_set.cpp @@ -474,6 +477,7 @@ src/tinygettext/stk_file_system.hpp src/tinygettext/tgt_log.hpp src/tinygettext/tinygettext.hpp src/tracks/ambient_light_sphere.hpp +src/tracks/battle_graph.hpp src/tracks/bezier_curve.hpp src/tracks/check_cannon.hpp src/tracks/check_goal.hpp @@ -484,6 +488,8 @@ src/tracks/check_sphere.hpp src/tracks/check_structure.hpp src/tracks/graph_node.hpp src/tracks/lod_node_loader.hpp +src/tracks/navmesh.hpp +src/tracks/nav_poly.hpp src/tracks/quad_graph.hpp src/tracks/quad.hpp src/tracks/quad_set.hpp From 442ceee8365cb32cec7a337528dfcef6972e1ce0 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Wed, 2 Oct 2013 03:51:42 +0000 Subject: [PATCH 009/170] Fixed compilation issues on linux. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14183 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/navmesh.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tracks/navmesh.hpp b/src/tracks/navmesh.hpp index 1bd94fc8a..2da3074d7 100644 --- a/src/tracks/navmesh.hpp +++ b/src/tracks/navmesh.hpp @@ -51,8 +51,8 @@ private: unsigned int m_n_polys; unsigned int m_nvp; - void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const; - //void NavMesh::readFace(const XMLNode *xml, Vec3* result) const; + void readVertex(const XMLNode *xml, Vec3* result) const; + //void readFace(const XMLNode *xml, Vec3* result) const; NavMesh(const std::string &filename); ~NavMesh(); @@ -102,4 +102,4 @@ public: }; -#endif \ No newline at end of file +#endif From 6cc28c01de2a0c61f844fdce79088194c100f2a9 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 2 Oct 2013 04:03:22 +0000 Subject: [PATCH 010/170] Forgot to clean up debug mesh and navmesh. Also moved debug camera a bit closer to the kart. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14184 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/graphics/camera.cpp | 2 +- src/tracks/battle_graph.cpp | 16 +++++++++++++++- src/tracks/battle_graph.hpp | 2 +- src/tracks/navmesh.cpp | 2 -- src/tracks/navmesh.hpp | 9 +++++++++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index 968170014..bc0e44b92 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -451,7 +451,7 @@ void Camera::update(float dt) { core::vector3df xyz = m_kart->getXYZ().toIrrVector(); m_camera->setTarget(xyz); - xyz.Y = xyz.Y+55; + xyz.Y = xyz.Y+40; xyz.Z -= 5.0f; m_camera->setPosition(xyz); // To view inside tunnels (FIXME 27>15 why??? makes no sense diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index 40850b312..9d1962ae5 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -22,6 +22,7 @@ #include #include +#include "config/user_config.hpp" #include "graphics/irr_driver.hpp" #include "tracks/navmesh.hpp" #include "utils/vec3.hpp" @@ -56,7 +57,10 @@ void BattleGraph::buildGraph(NavMesh* navmesh) } BattleGraph::~BattleGraph(void) { + NavMesh::destroy(); + if(UserConfigParams::m_track_debug) + cleanupDebugMesh(); } void BattleGraph::createMesh(bool enable_transparency, @@ -96,7 +100,7 @@ void BattleGraph::createMesh(bool enable_transparency, c.setAlpha(178); //c.setRed ((i%2) ? 255 : 0); //c.setBlue((i%3) ? 0 : 255); - c.setRed(i%256); + c.setRed(7*i%256); c.setBlue((2*i)%256); c.setGreen((3*i)%256); } @@ -167,3 +171,13 @@ void BattleGraph::createDebugMesh() #endif } // createDebugMesh + +void BattleGraph::cleanupDebugMesh() +{ + irr_driver->removeNode(m_node); + m_node = NULL; + // No need to call irr_driber->removeMeshFromCache, since the mesh + // was manually made and so never added to the mesh cache. + m_mesh->drop(); + m_mesh = NULL; +} \ No newline at end of file diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp index 772b23346..b057df7dd 100644 --- a/src/tracks/battle_graph.hpp +++ b/src/tracks/battle_graph.hpp @@ -85,7 +85,7 @@ public: unsigned int getNumNodes() const { return m_graph.size(); } void createDebugMesh(); - + void cleanupDebugMesh(); }; #endif \ No newline at end of file diff --git a/src/tracks/navmesh.cpp b/src/tracks/navmesh.cpp index 7d6526c84..e45a6d70b 100644 --- a/src/tracks/navmesh.cpp +++ b/src/tracks/navmesh.cpp @@ -123,8 +123,6 @@ NavMesh::NavMesh(const std::string &filename) NavMesh::~NavMesh() { - delete m_nav_mesh; - m_nav_mesh = NULL; } // ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/tracks/navmesh.hpp b/src/tracks/navmesh.hpp index 2da3074d7..5159173bf 100644 --- a/src/tracks/navmesh.hpp +++ b/src/tracks/navmesh.hpp @@ -67,7 +67,16 @@ public: new NavMesh(filename); } + static void destroy() + { + if(m_nav_mesh) + { + delete m_nav_mesh; + m_nav_mesh = NULL; + } + } + static NavMesh *get() { return m_nav_mesh; } const NavPoly& getNavPoly(int n) const From 5b98a8bcf09c81b198aa9d09e891d57372a3b36c Mon Sep 17 00:00:00 2001 From: hikerstk Date: Wed, 2 Oct 2013 04:15:42 +0000 Subject: [PATCH 011/170] Fixed unnecessary type cast (since definition of IMeshSceneNode was missing), fixed compiler warning. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14185 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/tracks/battle_graph.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index 9d1962ae5..efdf380f5 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -21,6 +21,7 @@ #include #include +#include #include "config/user_config.hpp" #include "graphics/irr_driver.hpp" @@ -41,7 +42,7 @@ void BattleGraph::buildGraph(NavMesh* navmesh) { unsigned int n_polys = navmesh->getNumberOfPolys(); m_graph.resize(n_polys); - for(int i=0; igetNavPoly(i); std::vector adjacents = navmesh->getAdjacentPolys(i); @@ -165,7 +166,7 @@ void BattleGraph::createDebugMesh() if(m_graph.size()<=0) return; // no debug output if not graph createMesh(/*enable_transparency*/true); - m_node = (scene::ISceneNode*)irr_driver->addMesh(m_mesh); + m_node = irr_driver->addMesh(m_mesh); #ifdef DEBUG m_node->setName("track-debug-mesh"); #endif @@ -180,4 +181,4 @@ void BattleGraph::cleanupDebugMesh() // was manually made and so never added to the mesh cache. m_mesh->drop(); m_mesh = NULL; -} \ No newline at end of file +} From d3f92542df57f2df68200fb029867616ea9f6102 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 9 Oct 2013 07:26:55 +0000 Subject: [PATCH 012/170] Split AIBaseController to AIBaseLapController and AIBaseController. AIBaseLapController inherits from AIBaseController. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14223 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/ide/vc10/supertuxkart.vcxproj | 6 +- src/ide/vc10/supertuxkart.vcxproj.filters | 18 +- src/karts/controller/ai_base_controller.cpp | 485 +--------------- src/karts/controller/ai_base_controller.hpp | 86 +-- .../controller/ai_base_lap_controller.cpp | 518 ++++++++++++++++++ .../controller/ai_base_lap_controller.hpp | 121 ++++ src/karts/controller/ai_properties.hpp | 2 +- src/karts/controller/end_controller.cpp | 8 +- src/karts/controller/end_controller.hpp | 4 +- src/karts/controller/skidding_ai.cpp | 16 +- src/karts/controller/skidding_ai.hpp | 4 +- src/main.cpp | 4 +- 12 files changed, 680 insertions(+), 592 deletions(-) create mode 100644 src/karts/controller/ai_base_lap_controller.cpp create mode 100644 src/karts/controller/ai_base_lap_controller.hpp diff --git a/src/ide/vc10/supertuxkart.vcxproj b/src/ide/vc10/supertuxkart.vcxproj index 4b8d59188..a4c176d7d 100644 --- a/src/ide/vc10/supertuxkart.vcxproj +++ b/src/ide/vc10/supertuxkart.vcxproj @@ -229,6 +229,7 @@ + @@ -357,7 +358,7 @@ - + @@ -480,6 +481,7 @@ + @@ -633,7 +635,7 @@ - + diff --git a/src/ide/vc10/supertuxkart.vcxproj.filters b/src/ide/vc10/supertuxkart.vcxproj.filters index 410e2c423..dbaafeb09 100644 --- a/src/ide/vc10/supertuxkart.vcxproj.filters +++ b/src/ide/vc10/supertuxkart.vcxproj.filters @@ -372,9 +372,6 @@ Source Files\karts - - Source Files\karts\controller - Source Files\karts\controller @@ -882,6 +879,12 @@ Source Files\tracks + + Source Files\karts\controller + + + Source Files\karts\controller + @@ -1175,9 +1178,6 @@ Header Files\karts - - Header Files\karts\controller - Header Files\karts\controller @@ -1688,5 +1688,11 @@ Header Files\tracks + + Header Files\karts\controller + + + Header Files\karts\controller + \ No newline at end of file diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index 9d82dc685..bda0ad3d2 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -23,496 +23,13 @@ #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" -#include "karts/skidding_properties.hpp" #include "karts/controller/ai_properties.hpp" -#include "modes/linear_world.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" -bool AIBaseController::m_ai_debug = false; -/** -This is the base class for all AIs. At this stage there are two similar -AIs: one is the SkiddingAI, which is the AI used in lap based races -(including follow-the-leader mode), the other one is the end controller, -I.e. the controller that takes over from a player (or AI) when the race is -finished. - -This base class defines some basic operations: -- It takes care on which part of the QuadGraph the AI currently is. -- It determines which path the AI should take (in case of shortcuts - or forks in the road). - -At race start and every time a new lap is started, the AI will compute the -path the kart is taking this lap (computePath). At this stage the decision -which road in case of shortcut to take is purely random. It stores the -information in two arrays: - m_successor_index[i] stores which successor to take from node i. - The successor is a number between 0 and number_of_successors - 1. - m_next_node_index[i] stores the actual index of the graph node that - follows after node i. -Depending on operation one of the other data is more useful, so this -class stores both information to avoid looking it up over and over. -Once this is done (still in computePath), the array m_all_look_aheads is -computed. This array stores for each quad a list of the next (atm) 10 quads. -This is used when the AI is selecting where to drive next, and it will just -pass the list of next quads to findRoadSector. - -Note that the quad graph information is stored for every quad in the quad -graph, even if the quad is not on the path chosen. This is necessary since -it can happen that a kart ends up on a path not choses (e.g. perhaps it was -pushed on that part, or couldn't get a sharp corner). - -In update(), which gets called one per frame per AI, this object will -determine the quad the kart is currently on (which is then used to determine -where the kart will be driving to). This uses the m_all_look_aheads to -speed up this process (since the kart is likely to be either on the same -quad as it was before, or the next quad in the m_all_look_aheads list). - -It will also check if the kart is stuck: -this is done by maintaining a list of times when the kart hits the track. If -(atm) more than 3 collisions happen in 1.5 seconds, the kart is considered -stuck and will trigger a rescue (due to the pushback from the track it will -take some time if a kart is really stuck before it will hit the track again). - -This base class also contains some convenience functions which are useful -in all AIs, e.g.: -- steerToPoint: determine the steering angle to use depending on the - current location and the point the kart is driving to. -- normalizeAngle: To normalise the steering angle to be in [-PI,PI]. -- setSteering: Converts the steering angle into a steering fraction - in [-1,1]. - -*/ AIBaseController::AIBaseController(AbstractKart *kart, StateManager::ActivePlayer *player) : Controller(kart, player) { - m_kart = kart; - m_kart_length = m_kart->getKartLength(); - m_kart_width = m_kart->getKartWidth(); - m_ai_properties = - m_kart->getKartProperties()->getAIPropertiesForDifficulty(); - - if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES && - race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER) - { - m_world = dynamic_cast(World::getWorld()); - m_track = m_world->getTrack(); - computePath(); - } - else - { - // Those variables are not defined in a battle mode (m_world is - // a linear world, since it assumes the existance of drivelines) - m_world = NULL; - m_track = NULL; - m_next_node_index.clear(); - m_all_look_aheads.clear(); - m_successor_index.clear(); - } // if battle mode - // Don't call our own setControllerName, since this will add a - // billboard showing 'aibasecontroller' to the kar. - Controller::setControllerName("AIBaseController"); -} // AIBaseController - -//----------------------------------------------------------------------------- -void AIBaseController::reset() -{ - m_stuck_trigger_rescue = false; - m_collision_times.clear(); -} // reset - -//----------------------------------------------------------------------------- -/** In debug mode when the user specified --ai-debug on the command line set - * the name of the controller as on-screen text, so that the different AI - * controllers can be distinguished. - * \param name Name of the controller. -*/ -void AIBaseController::setControllerName(const std::string &name) -{ -#ifdef DEBUG - if(m_ai_debug && !UserConfigParams::m_camera_debug) - m_kart->setOnScreenText(core::stringw(name.c_str()).c_str()); -#endif - Controller::setControllerName(name); -} // setControllerName - -//----------------------------------------------------------------------------- -/** Triggers a recomputation of the path to use, so that the AI does not - * always use the same way. - */ -void AIBaseController::newLap(int lap) -{ - if(lap>0) - { - computePath(); - } -} // newLap - -//----------------------------------------------------------------------------- -/** Computes a path for the AI to follow. This function is called at race - * start and every time a new lap is started. Recomputing the path every - * time will mean that the kart will not always take the same path, but - * (potentially) vary from lap to lap. At this stage the decision is done - * randomly. The AI could be improved by collecting more information about - * each branch of a track, and selecting the 'appropriate' one (e.g. if the - * AI is far ahead, chose a longer/slower path). - */ -void AIBaseController::computePath() -{ - m_next_node_index.resize(QuadGraph::get()->getNumNodes()); - m_successor_index.resize(QuadGraph::get()->getNumNodes()); - std::vector next; - for(unsigned int i=0; igetNumNodes(); i++) - { - next.clear(); - // Get all successors the AI is allowed to take. - QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true); - // In case of short cuts hidden for the AI it can be that a node - // might not have a successor (since the first and last edge of - // a hidden shortcut is ignored). Since in the case that the AI - // ends up on a short cut (e.g. by accident) and doesn't have an - // allowed way to drive, it should still be able to drive, so add - // the non-AI successors of that node in this case. - if(next.size()==0) - QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false); - // For now pick one part on random, which is not adjusted during the - // race. Long term statistics might be gathered to determine the - // best way, potentially depending on race position etc. - int r = rand(); - int indx = (int)( r / ((float)(RAND_MAX)+1.0f) * next.size() ); - // In case of rounding errors0 - if(indx>=(int)next.size()) indx--; - m_successor_index[i] = indx; - assert(indx <(int)next.size() && indx>=0); - m_next_node_index[i] = next[indx]; - } - - const unsigned int look_ahead=10; - // Now compute for each node in the graph the list of the next 'look_ahead' - // graph nodes. This is the list of node that is tested in checkCrashes. - // If the look_ahead is too big, the AI can skip loops (see - // QuadGraph::findRoadSector for details), if it's too short the AI won't - // find too good a driveline. Note that in general this list should - // be computed recursively, but since the AI for now is using only - // (randomly picked) path this is fine - m_all_look_aheads.resize(QuadGraph::get()->getNumNodes()); - for(unsigned int i=0; igetNumNodes(); i++) - { - std::vector l; - int current = i; - for(unsigned int j=0; jfindRoadSector(m_kart->getXYZ(), &m_track_node, - &m_all_look_aheads[m_track_node]); - } - // If we can't find a proper place on the track, to a broader search - // on off-track locations. - if(m_track_node==QuadGraph::UNKNOWN_SECTOR) - { - m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); - } - // IF the AI is off track (or on a branch of the track it did not - // select to be on), keep the old position. - if(m_track_node==QuadGraph::UNKNOWN_SECTOR || - m_next_node_index[m_track_node]==-1) - m_track_node = old_node; - } -} // update - -//----------------------------------------------------------------------------- -/** This is called when the kart crashed with the terrain. This subroutine - * tries to detect if the AI is stuck by determining if a certain number - * of collisions happened in a certain amount of time, and if so rescues - * the kart. - * \paran m Pointer to the material that was hit (NULL if no specific - * material was used for the part of the track that was hit). - */ -void AIBaseController::crashed(const Material *m) -{ - // Defines how many collision in what time will trigger a rescue. - // Note that typically it takes ~0.5 seconds for the AI to hit - // the track again if it is stuck (i.e. time for the push back plus - // time for the AI to accelerate and hit the terrain again). - const unsigned int NUM_COLLISION = 3; - const float COLLISION_TIME = 1.5f; - - float time = World::getWorld()->getTime(); - if(m_collision_times.size()==0) - { - m_collision_times.push_back(time); - return; - } - - // Filter out multiple collisions report caused by single collision - // (bullet can report a collision more than once per frame, and - // resolving it can take a few frames as well, causing more reported - // collisions to happen). The time of 0.2 seconds was experimentally - // found, typically it takes 0.5 seconds for a kart to be pushed back - // from the terrain and accelerate to hit the same terrain again. - if(time - m_collision_times.back() < 0.2f) - return; - - - // Remove all outdated entries, i.e. entries that are older than the - // collision time plus 1 second. Older entries must be deleted, - // otherwise a collision that happened (say) 10 seconds ago could - // contribute to a stuck condition. - while(m_collision_times.size()>0 && - time - m_collision_times[0] > 1.0f+COLLISION_TIME) - m_collision_times.erase(m_collision_times.begin()); - - m_collision_times.push_back(time); - - // Now detect if there are enough collision records in the - // specified time interval. - if(time - m_collision_times.front() > COLLISION_TIME - && m_collision_times.size()>=NUM_COLLISION) - { - // We can't call m_kart->forceRescue here, since crased() is - // called during physics processing, and forceRescue() removes the - // chassis from the physics world, which would then cause - // inconsistencies and potentially a crash during the physics - // processing. So only set a flag, which is tested during update. - m_stuck_trigger_rescue = true; - } - -} // crashed(Material) - -//----------------------------------------------------------------------------- -/** Returns the next sector of the given sector index. This is used - * for branches in the quad graph to select which way the AI kart should - * go. This is a very simple implementation that always returns the first - * successor, but it can be overridden to allow a better selection. - * \param index Index of the graph node for which the successor is searched. - * \return Returns the successor of this graph node. - */ -unsigned int AIBaseController::getNextSector(unsigned int index) -{ - std::vector successors; - QuadGraph::get()->getSuccessors(index, successors); - return successors[0]; -} // getNextSector - -//----------------------------------------------------------------------------- -/** This function steers towards a given angle. It also takes a plunger - ** attached to this kart into account by modifying the actual steer angle - * somewhat to simulate driving without seeing. - */ -float AIBaseController::steerToAngle(const unsigned int sector, - const float add_angle) -{ - float angle = QuadGraph::get()->getAngleToNext(sector, - getNextSector(sector)); - - //Desired angle minus current angle equals how many angles to turn - float steer_angle = angle - m_kart->getHeading(); - - if(m_kart->getBlockedByPlungerTime()>0) - steer_angle += add_angle*0.2f; - else - steer_angle += add_angle; - steer_angle = normalizeAngle( steer_angle ); - - return steer_angle; -} // steerToAngle - -//----------------------------------------------------------------------------- -/** Computes the steering angle to reach a certain point. The function will - * request steering by setting the steering angle to maximum steer angle - * times skidding factor. - * \param point Point to steer towards. - * \param skidding_factor Increase factor for steering when skidding. - * \return Steer angle to use to reach this point. - */ -float AIBaseController::steerToPoint(const Vec3 &point) -{ - - // First translate and rotate the point the AI is aiming - // at into the kart's local coordinate system. - btQuaternion q(btVector3(0,1,0), -m_kart->getHeading()); - Vec3 p = point - m_kart->getXYZ(); - Vec3 lc = quatRotate(q, p); - - // The point the kart is aiming at can be reached 'incorrectly' if the - // point is below the y=x line: Instead of aiming at that point directly - // the point will be reached on its way 'back' after a more than 90 - // degree turn in the circle, i.e.: - // | So the point p (belolw the y=x line) can not be - // | ---\ reached on any circle directly, so it is reached - // | / \ on the indicated way. Since this is not the way - // |/ p we expect a kart to drive (it will result in the - // +-------------- kart doing slaloms, not driving straight), the - // kart will trigger skidding to allow for sharper turns, and hopefully - // the situation will change so that the point p can then be reached - // with a normal turn (it usually works out this way quite easily). - if(fabsf(lc.getX()) > fabsf(lc.getZ())) - { - // Explicitely set the steering angle high enough to that the - // steer function will request skidding. 0.1 is added in case - // of floating point errors. - if(lc.getX()>0) - return m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold+0.1f; - else - return -m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold-0.1f; - } - - // Now compute the nexessary radius for the turn. After getting the - // kart local coordinates for the point to aim at, the kart is at - // (0,0) facing straight ahead. The center of the rotation is then - // on the X axis and can be computed by the fact that the distance - // to the kart and to the point to aim at must be the same: - // r*r = (r-x)*(r-x) + y*y - // where r is the radius (= position on the X axis), and x, y are the - // local coordinates of the point to aim at. Solving for r - // results in r = (x*x+y*y)/2x - float radius = (lc.getX()*lc.getX() + lc.getZ()*lc.getZ()) - / (2.0f*lc.getX()); - - // sin(steern_angle) = wheel_base / radius: - float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius; - - // If the wheel base is too long (i.e. the minimum radius is too large - // to actually reach the target), make sure that skidding is used - if(sin_steer_angle <= -1.0f) - return -m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold-0.1f; - if(sin_steer_angle >= 1.0f) - return m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold+0.1f; - float steer_angle = asin(sin_steer_angle); - - // After doing the exact computation, we now return an 'oversteered' - // value. This actually helps in making tighter turns, and also in - // very tight turns on narrow roads (where following the circle might - // actually take the kart off track) it forces smaller turns. - // It does not actually hurt to steer too much, since the steering - // will be adjusted every frame. - return steer_angle*2.0f; -} // steerToPoint - -//----------------------------------------------------------------------------- -/** Normalises an angle to be between -pi and _ pi. - * \param angle Angle to normalise. - * \return Normalised angle. - */ -float AIBaseController::normalizeAngle(float angle) -{ - // Add an assert here since we had cases in which an invalid angle - // was given, resulting in an endless loop (floating point precision, - // e.g.: 1E17 - 2*M_PI = 1E17 - assert(angle >= -4*M_PI && angle <= 4*M_PI); - while( angle > 2*M_PI ) angle -= 2*M_PI; - while( angle < -2*M_PI ) angle += 2*M_PI; - - if( angle > M_PI ) angle -= 2*M_PI; - else if( angle < -M_PI ) angle += 2*M_PI; - - return angle; -} // normalizeAngle - -//----------------------------------------------------------------------------- -/** Converts the steering angle to a lr steering in the range of -1 to 1. - * If the steering angle is too great, it will also trigger skidding. This - * function uses a 'time till full steer' value specifying the time it takes - * for the wheel to reach full left/right steering similar to player karts - * when using a digital input device. The parameter is defined in the kart - * properties and helps somewhat to make AI karts more 'pushable' (since - * otherwise the karts counter-steer to fast). - * It also takes the effect of a plunger into account by restricting the - * actual steer angle to 50% of the maximum. - * \param angle Steering angle. - * \param dt Time step. - */ -void AIBaseController::setSteering(float angle, float dt) -{ - float steer_fraction = angle / m_kart->getMaxSteerAngle(); - if(!doSkid(steer_fraction)) - m_controls->m_skid = KartControl::SC_NONE; - else - m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT - : KartControl::SC_LEFT; - float old_steer = m_controls->m_steer; - - if (steer_fraction > 1.0f) steer_fraction = 1.0f; - else if(steer_fraction < -1.0f) steer_fraction = -1.0f; - - if(m_kart->getBlockedByPlungerTime()>0) - { - if (steer_fraction > 0.5f) steer_fraction = 0.5f; - else if(steer_fraction < -0.5f) steer_fraction = -0.5f; - } - - // The AI has its own 'time full steer' value (which is the time - float max_steer_change = dt/m_ai_properties->m_time_full_steer; - if(old_steer < steer_fraction) - { - m_controls->m_steer = (old_steer+max_steer_change > steer_fraction) - ? steer_fraction : old_steer+max_steer_change; - } - else - { - m_controls->m_steer = (old_steer-max_steer_change < steer_fraction) - ? steer_fraction : old_steer-max_steer_change; - } -} // setSteering - -// ---------------------------------------------------------------------------- -/** Determines if the kart should skid. The base implementation enables - * skidding if a sharp turn is needed (which is for the old skidding - * implementation). - * \param steer_fraction The steering fraction as computed by the - * AIBaseController. - * \return True if the kart should skid. - */ -bool AIBaseController::doSkid(float steer_fraction) -{ - // Disable skidding when a plunger is in the face - if(m_kart->getBlockedByPlungerTime()>0) return false; - - // FIXME: Disable skidding for now if the new skidding - // code is activated, since the AI can not handle this - // properly. - if(m_kart->getKartProperties()->getSkiddingProperties() - ->getSkidVisualTime()>0) - return false; - - // Otherwise return if we need a sharp turn (which is - // for the old skidding implementation). - return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold; -} // doSkid -// ------------------------------------------------------------------------ -/** Certain AI levels will not receive a slipstream bonus in order to - * be not as hard. - */ -bool AIBaseController::disableSlipstreamBonus() const -{ - return m_ai_properties->disableSlipstreamUsage(); -} // disableSlipstreamBonus +} \ No newline at end of file diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 3fbeda17c..d8a89420c 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -24,98 +24,22 @@ class AIProperties; class LinearWorld; class QuadGraph; +class BattleGraph; class Track; class Vec3; -/** A base class for all AI karts. This class basically provides some - * common low level functions. - * \ingroup controller - */ class AIBaseController : public Controller { -private: - /** Stores the last N times when a collision happened. This is used - * to detect when the AI is stuck, i.e. N collisions happened in - * a certain period of time. */ - std::vector m_collision_times; - /** A flag that is set during the physics processing to indicate that - * this kart is stuck and needs to be rescued. */ - bool m_stuck_trigger_rescue; -protected: - /** Length of the kart, storing it here saves many function calls. */ - float m_kart_length; - /** Cache width of kart. */ - float m_kart_width; - /** Keep a pointer to the track to reduce calls */ - Track *m_track; - /** Keep a pointer to world. */ - LinearWorld *m_world; - - /** A pointer to the AI properties for this kart. */ - const AIProperties *m_ai_properties; - - /** The current node the kart is on. This can be different from the value - * in LinearWorld, since it takes the chosen path of the AI into account - * (e.g. the closest point in LinearWorld might be on a branch not - * chosen by the AI). */ - int m_track_node; - - /** Which of the successors of a node was selected by the AI. */ - std::vector m_successor_index; - /** For each node in the graph this list contains the chosen next node. - * For normal lap track without branches we always have - * m_next_node_index[i] = (i+1) % size; - * but if a branch is possible, the AI will select one option here. - * If the node is not used, m_next_node_index will be -1. */ - std::vector m_next_node_index; - /** For each graph node this list contains a list of the next X - * graph nodes. */ - std::vector > m_all_look_aheads; - - virtual void update (float delta) ; - virtual unsigned int getNextSector(unsigned int index); - virtual void newLap (int lap); - virtual void setControllerName(const std::string &name); - virtual void setSteering (float angle, float dt); - float steerToAngle (const unsigned int sector, const float angle); - float steerToPoint (const Vec3 &point); - float normalizeAngle(float angle); - void computePath(); - virtual bool doSkid(float steer_fraction); - // ------------------------------------------------------------------------ - /** Nothing special to do when the race is finished. */ - virtual void raceFinished() {}; - // ------------------------------------------------------------------------ - /** This can be called to detect if the kart is stuck (i.e. repeatedly - * hitting part of the track). */ - bool isStuck() const { return m_stuck_trigger_rescue; } - - static bool m_ai_debug; public: - AIBaseController(AbstractKart *kart, + + AIBaseController(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); virtual ~AIBaseController() {}; - virtual void reset(); - static void enableDebug() {m_ai_debug = true; } - virtual void crashed(const AbstractKart *k) {}; - virtual void crashed(const Material *m); - virtual void handleZipper(bool play_sound) {}; - virtual void finishedRace(float time) {}; - virtual void collectedItem(const Item &item, int add_info=-1, - float previous_energy=0) {}; - virtual void setPosition(int p) {}; - virtual bool isNetworkController() const { return false; } - virtual bool isPlayerController() const { return false; } - virtual void action(PlayerAction action, int value) {}; - virtual void skidBonusTriggered() {}; - virtual bool disableSlipstreamBonus() const; -}; // AIBaseController -#endif - -/* EOF */ +}; +#endif \ No newline at end of file diff --git a/src/karts/controller/ai_base_lap_controller.cpp b/src/karts/controller/ai_base_lap_controller.cpp new file mode 100644 index 000000000..d873cc945 --- /dev/null +++ b/src/karts/controller/ai_base_lap_controller.cpp @@ -0,0 +1,518 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2006-2009 Eduardo Hernandez Munoz +// Copyright (C) 2009, 2010 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 "karts/controller/ai_base_lap_controller.hpp" + +#include + +#include "karts/abstract_kart.hpp" +#include "karts/kart_properties.hpp" +#include "karts/skidding_properties.hpp" +#include "karts/controller/ai_properties.hpp" +#include "modes/linear_world.hpp" +#include "tracks/track.hpp" +#include "utils/constants.hpp" + +bool AIBaseLapController::m_ai_debug = false; + +/** +This is the base class for all AIs. At this stage there are two similar +AIs: one is the SkiddingAI, which is the AI used in lap based races +(including follow-the-leader mode), the other one is the end controller, +I.e. the controller that takes over from a player (or AI) when the race is +finished. + +This base class defines some basic operations: +- It takes care on which part of the QuadGraph the AI currently is. +- It determines which path the AI should take (in case of shortcuts + or forks in the road). + +At race start and every time a new lap is started, the AI will compute the +path the kart is taking this lap (computePath). At this stage the decision +which road in case of shortcut to take is purely random. It stores the +information in two arrays: + m_successor_index[i] stores which successor to take from node i. + The successor is a number between 0 and number_of_successors - 1. + m_next_node_index[i] stores the actual index of the graph node that + follows after node i. +Depending on operation one of the other data is more useful, so this +class stores both information to avoid looking it up over and over. +Once this is done (still in computePath), the array m_all_look_aheads is +computed. This array stores for each quad a list of the next (atm) 10 quads. +This is used when the AI is selecting where to drive next, and it will just +pass the list of next quads to findRoadSector. + +Note that the quad graph information is stored for every quad in the quad +graph, even if the quad is not on the path chosen. This is necessary since +it can happen that a kart ends up on a path not choses (e.g. perhaps it was +pushed on that part, or couldn't get a sharp corner). + +In update(), which gets called one per frame per AI, this object will +determine the quad the kart is currently on (which is then used to determine +where the kart will be driving to). This uses the m_all_look_aheads to +speed up this process (since the kart is likely to be either on the same +quad as it was before, or the next quad in the m_all_look_aheads list). + +It will also check if the kart is stuck: +this is done by maintaining a list of times when the kart hits the track. If +(atm) more than 3 collisions happen in 1.5 seconds, the kart is considered +stuck and will trigger a rescue (due to the pushback from the track it will +take some time if a kart is really stuck before it will hit the track again). + +This base class also contains some convenience functions which are useful +in all AIs, e.g.: +- steerToPoint: determine the steering angle to use depending on the + current location and the point the kart is driving to. +- normalizeAngle: To normalise the steering angle to be in [-PI,PI]. +- setSteering: Converts the steering angle into a steering fraction + in [-1,1]. + +*/ +AIBaseLapController::AIBaseLapController(AbstractKart *kart, + StateManager::ActivePlayer *player) + : AIBaseController(kart, player) +{ + m_kart = kart; + m_kart_length = m_kart->getKartLength(); + m_kart_width = m_kart->getKartWidth(); + m_ai_properties = + m_kart->getKartProperties()->getAIPropertiesForDifficulty(); + + if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES && + race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER) + { + m_world = dynamic_cast(World::getWorld()); + m_track = m_world->getTrack(); + computePath(); + } + else + { + // Those variables are not defined in a battle mode (m_world is + // a linear world, since it assumes the existance of drivelines) + m_world = NULL; + m_track = NULL; + m_next_node_index.clear(); + m_all_look_aheads.clear(); + m_successor_index.clear(); + } // if battle mode + // Don't call our own setControllerName, since this will add a + // billboard showing 'AIBaseLapController' to the kar. + Controller::setControllerName("AIBaseLapController"); +} // AIBaseLapController + +//----------------------------------------------------------------------------- +void AIBaseLapController::reset() +{ + m_stuck_trigger_rescue = false; + m_collision_times.clear(); +} // reset + +//----------------------------------------------------------------------------- +/** In debug mode when the user specified --ai-debug on the command line set + * the name of the controller as on-screen text, so that the different AI + * controllers can be distinguished. + * \param name Name of the controller. +*/ +void AIBaseLapController::setControllerName(const std::string &name) +{ +#ifdef DEBUG + if(m_ai_debug && !UserConfigParams::m_camera_debug) + m_kart->setOnScreenText(core::stringw(name.c_str()).c_str()); +#endif + Controller::setControllerName(name); +} // setControllerName + +//----------------------------------------------------------------------------- +/** Triggers a recomputation of the path to use, so that the AI does not + * always use the same way. + */ +void AIBaseLapController::newLap(int lap) +{ + if(lap>0) + { + computePath(); + } +} // newLap + +//----------------------------------------------------------------------------- +/** Computes a path for the AI to follow. This function is called at race + * start and every time a new lap is started. Recomputing the path every + * time will mean that the kart will not always take the same path, but + * (potentially) vary from lap to lap. At this stage the decision is done + * randomly. The AI could be improved by collecting more information about + * each branch of a track, and selecting the 'appropriate' one (e.g. if the + * AI is far ahead, chose a longer/slower path). + */ +void AIBaseLapController::computePath() +{ + m_next_node_index.resize(QuadGraph::get()->getNumNodes()); + m_successor_index.resize(QuadGraph::get()->getNumNodes()); + std::vector next; + for(unsigned int i=0; igetNumNodes(); i++) + { + next.clear(); + // Get all successors the AI is allowed to take. + QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true); + // In case of short cuts hidden for the AI it can be that a node + // might not have a successor (since the first and last edge of + // a hidden shortcut is ignored). Since in the case that the AI + // ends up on a short cut (e.g. by accident) and doesn't have an + // allowed way to drive, it should still be able to drive, so add + // the non-AI successors of that node in this case. + if(next.size()==0) + QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false); + // For now pick one part on random, which is not adjusted during the + // race. Long term statistics might be gathered to determine the + // best way, potentially depending on race position etc. + int r = rand(); + int indx = (int)( r / ((float)(RAND_MAX)+1.0f) * next.size() ); + // In case of rounding errors0 + if(indx>=(int)next.size()) indx--; + m_successor_index[i] = indx; + assert(indx <(int)next.size() && indx>=0); + m_next_node_index[i] = next[indx]; + } + + const unsigned int look_ahead=10; + // Now compute for each node in the graph the list of the next 'look_ahead' + // graph nodes. This is the list of node that is tested in checkCrashes. + // If the look_ahead is too big, the AI can skip loops (see + // QuadGraph::findRoadSector for details), if it's too short the AI won't + // find too good a driveline. Note that in general this list should + // be computed recursively, but since the AI for now is using only + // (randomly picked) path this is fine + m_all_look_aheads.resize(QuadGraph::get()->getNumNodes()); + for(unsigned int i=0; igetNumNodes(); i++) + { + std::vector l; + int current = i; + for(unsigned int j=0; jfindRoadSector(m_kart->getXYZ(), &m_track_node, + &m_all_look_aheads[m_track_node]); + } + // If we can't find a proper place on the track, to a broader search + // on off-track locations. + if(m_track_node==QuadGraph::UNKNOWN_SECTOR) + { + m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + } + // IF the AI is off track (or on a branch of the track it did not + // select to be on), keep the old position. + if(m_track_node==QuadGraph::UNKNOWN_SECTOR || + m_next_node_index[m_track_node]==-1) + m_track_node = old_node; + } +} // update + +//----------------------------------------------------------------------------- +/** This is called when the kart crashed with the terrain. This subroutine + * tries to detect if the AI is stuck by determining if a certain number + * of collisions happened in a certain amount of time, and if so rescues + * the kart. + * \paran m Pointer to the material that was hit (NULL if no specific + * material was used for the part of the track that was hit). + */ +void AIBaseLapController::crashed(const Material *m) +{ + // Defines how many collision in what time will trigger a rescue. + // Note that typically it takes ~0.5 seconds for the AI to hit + // the track again if it is stuck (i.e. time for the push back plus + // time for the AI to accelerate and hit the terrain again). + const unsigned int NUM_COLLISION = 3; + const float COLLISION_TIME = 1.5f; + + float time = World::getWorld()->getTime(); + if(m_collision_times.size()==0) + { + m_collision_times.push_back(time); + return; + } + + // Filter out multiple collisions report caused by single collision + // (bullet can report a collision more than once per frame, and + // resolving it can take a few frames as well, causing more reported + // collisions to happen). The time of 0.2 seconds was experimentally + // found, typically it takes 0.5 seconds for a kart to be pushed back + // from the terrain and accelerate to hit the same terrain again. + if(time - m_collision_times.back() < 0.2f) + return; + + + // Remove all outdated entries, i.e. entries that are older than the + // collision time plus 1 second. Older entries must be deleted, + // otherwise a collision that happened (say) 10 seconds ago could + // contribute to a stuck condition. + while(m_collision_times.size()>0 && + time - m_collision_times[0] > 1.0f+COLLISION_TIME) + m_collision_times.erase(m_collision_times.begin()); + + m_collision_times.push_back(time); + + // Now detect if there are enough collision records in the + // specified time interval. + if(time - m_collision_times.front() > COLLISION_TIME + && m_collision_times.size()>=NUM_COLLISION) + { + // We can't call m_kart->forceRescue here, since crased() is + // called during physics processing, and forceRescue() removes the + // chassis from the physics world, which would then cause + // inconsistencies and potentially a crash during the physics + // processing. So only set a flag, which is tested during update. + m_stuck_trigger_rescue = true; + } + +} // crashed(Material) + +//----------------------------------------------------------------------------- +/** Returns the next sector of the given sector index. This is used + * for branches in the quad graph to select which way the AI kart should + * go. This is a very simple implementation that always returns the first + * successor, but it can be overridden to allow a better selection. + * \param index Index of the graph node for which the successor is searched. + * \return Returns the successor of this graph node. + */ +unsigned int AIBaseLapController::getNextSector(unsigned int index) +{ + std::vector successors; + QuadGraph::get()->getSuccessors(index, successors); + return successors[0]; +} // getNextSector + +//----------------------------------------------------------------------------- +/** This function steers towards a given angle. It also takes a plunger + ** attached to this kart into account by modifying the actual steer angle + * somewhat to simulate driving without seeing. + */ +float AIBaseLapController::steerToAngle(const unsigned int sector, + const float add_angle) +{ + float angle = QuadGraph::get()->getAngleToNext(sector, + getNextSector(sector)); + + //Desired angle minus current angle equals how many angles to turn + float steer_angle = angle - m_kart->getHeading(); + + if(m_kart->getBlockedByPlungerTime()>0) + steer_angle += add_angle*0.2f; + else + steer_angle += add_angle; + steer_angle = normalizeAngle( steer_angle ); + + return steer_angle; +} // steerToAngle + +//----------------------------------------------------------------------------- +/** Computes the steering angle to reach a certain point. The function will + * request steering by setting the steering angle to maximum steer angle + * times skidding factor. + * \param point Point to steer towards. + * \param skidding_factor Increase factor for steering when skidding. + * \return Steer angle to use to reach this point. + */ +float AIBaseLapController::steerToPoint(const Vec3 &point) +{ + + // First translate and rotate the point the AI is aiming + // at into the kart's local coordinate system. + btQuaternion q(btVector3(0,1,0), -m_kart->getHeading()); + Vec3 p = point - m_kart->getXYZ(); + Vec3 lc = quatRotate(q, p); + + // The point the kart is aiming at can be reached 'incorrectly' if the + // point is below the y=x line: Instead of aiming at that point directly + // the point will be reached on its way 'back' after a more than 90 + // degree turn in the circle, i.e.: + // | So the point p (belolw the y=x line) can not be + // | ---\ reached on any circle directly, so it is reached + // | / \ on the indicated way. Since this is not the way + // |/ p we expect a kart to drive (it will result in the + // +-------------- kart doing slaloms, not driving straight), the + // kart will trigger skidding to allow for sharper turns, and hopefully + // the situation will change so that the point p can then be reached + // with a normal turn (it usually works out this way quite easily). + if(fabsf(lc.getX()) > fabsf(lc.getZ())) + { + // Explicitely set the steering angle high enough to that the + // steer function will request skidding. 0.1 is added in case + // of floating point errors. + if(lc.getX()>0) + return m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold+0.1f; + else + return -m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold-0.1f; + } + + // Now compute the nexessary radius for the turn. After getting the + // kart local coordinates for the point to aim at, the kart is at + // (0,0) facing straight ahead. The center of the rotation is then + // on the X axis and can be computed by the fact that the distance + // to the kart and to the point to aim at must be the same: + // r*r = (r-x)*(r-x) + y*y + // where r is the radius (= position on the X axis), and x, y are the + // local coordinates of the point to aim at. Solving for r + // results in r = (x*x+y*y)/2x + float radius = (lc.getX()*lc.getX() + lc.getZ()*lc.getZ()) + / (2.0f*lc.getX()); + + // sin(steern_angle) = wheel_base / radius: + float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius; + + // If the wheel base is too long (i.e. the minimum radius is too large + // to actually reach the target), make sure that skidding is used + if(sin_steer_angle <= -1.0f) + return -m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold-0.1f; + if(sin_steer_angle >= 1.0f) + return m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold+0.1f; + float steer_angle = asin(sin_steer_angle); + + // After doing the exact computation, we now return an 'oversteered' + // value. This actually helps in making tighter turns, and also in + // very tight turns on narrow roads (where following the circle might + // actually take the kart off track) it forces smaller turns. + // It does not actually hurt to steer too much, since the steering + // will be adjusted every frame. + return steer_angle*2.0f; +} // steerToPoint + +//----------------------------------------------------------------------------- +/** Normalises an angle to be between -pi and _ pi. + * \param angle Angle to normalise. + * \return Normalised angle. + */ +float AIBaseLapController::normalizeAngle(float angle) +{ + // Add an assert here since we had cases in which an invalid angle + // was given, resulting in an endless loop (floating point precision, + // e.g.: 1E17 - 2*M_PI = 1E17 + assert(angle >= -4*M_PI && angle <= 4*M_PI); + while( angle > 2*M_PI ) angle -= 2*M_PI; + while( angle < -2*M_PI ) angle += 2*M_PI; + + if( angle > M_PI ) angle -= 2*M_PI; + else if( angle < -M_PI ) angle += 2*M_PI; + + return angle; +} // normalizeAngle + +//----------------------------------------------------------------------------- +/** Converts the steering angle to a lr steering in the range of -1 to 1. + * If the steering angle is too great, it will also trigger skidding. This + * function uses a 'time till full steer' value specifying the time it takes + * for the wheel to reach full left/right steering similar to player karts + * when using a digital input device. The parameter is defined in the kart + * properties and helps somewhat to make AI karts more 'pushable' (since + * otherwise the karts counter-steer to fast). + * It also takes the effect of a plunger into account by restricting the + * actual steer angle to 50% of the maximum. + * \param angle Steering angle. + * \param dt Time step. + */ +void AIBaseLapController::setSteering(float angle, float dt) +{ + float steer_fraction = angle / m_kart->getMaxSteerAngle(); + if(!doSkid(steer_fraction)) + m_controls->m_skid = KartControl::SC_NONE; + else + m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT + : KartControl::SC_LEFT; + float old_steer = m_controls->m_steer; + + if (steer_fraction > 1.0f) steer_fraction = 1.0f; + else if(steer_fraction < -1.0f) steer_fraction = -1.0f; + + if(m_kart->getBlockedByPlungerTime()>0) + { + if (steer_fraction > 0.5f) steer_fraction = 0.5f; + else if(steer_fraction < -0.5f) steer_fraction = -0.5f; + } + + // The AI has its own 'time full steer' value (which is the time + float max_steer_change = dt/m_ai_properties->m_time_full_steer; + if(old_steer < steer_fraction) + { + m_controls->m_steer = (old_steer+max_steer_change > steer_fraction) + ? steer_fraction : old_steer+max_steer_change; + } + else + { + m_controls->m_steer = (old_steer-max_steer_change < steer_fraction) + ? steer_fraction : old_steer-max_steer_change; + } +} // setSteering + +// ---------------------------------------------------------------------------- +/** Determines if the kart should skid. The base implementation enables + * skidding if a sharp turn is needed (which is for the old skidding + * implementation). + * \param steer_fraction The steering fraction as computed by the + * AIBaseLapController. + * \return True if the kart should skid. + */ +bool AIBaseLapController::doSkid(float steer_fraction) +{ + // Disable skidding when a plunger is in the face + if(m_kart->getBlockedByPlungerTime()>0) return false; + + // FIXME: Disable skidding for now if the new skidding + // code is activated, since the AI can not handle this + // properly. + if(m_kart->getKartProperties()->getSkiddingProperties() + ->getSkidVisualTime()>0) + return false; + + // Otherwise return if we need a sharp turn (which is + // for the old skidding implementation). + return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold; +} // doSkid +// ------------------------------------------------------------------------ +/** Certain AI levels will not receive a slipstream bonus in order to + * be not as hard. + */ +bool AIBaseLapController::disableSlipstreamBonus() const +{ + return m_ai_properties->disableSlipstreamUsage(); +} // disableSlipstreamBonus diff --git a/src/karts/controller/ai_base_lap_controller.hpp b/src/karts/controller/ai_base_lap_controller.hpp new file mode 100644 index 000000000..3fdd0ee7b --- /dev/null +++ b/src/karts/controller/ai_base_lap_controller.hpp @@ -0,0 +1,121 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010 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 HEADER_AI_BASE_LAP_CONTROLLER_HPP +#define HEADER_AI_BASE_LAP_CONTROLLER_HPP + +#include "karts/controller/ai_base_controller.hpp" +#include "states_screens/state_manager.hpp" + +class AIProperties; +class LinearWorld; +class QuadGraph; +class Track; +class Vec3; + +/** A base class for all AI karts. This class basically provides some + * common low level functions. + * \ingroup controller + */ +class AIBaseLapController : public AIBaseController +{ +private: + /** Stores the last N times when a collision happened. This is used + * to detect when the AI is stuck, i.e. N collisions happened in + * a certain period of time. */ + std::vector m_collision_times; + + /** A flag that is set during the physics processing to indicate that + * this kart is stuck and needs to be rescued. */ + bool m_stuck_trigger_rescue; + +protected: + /** Length of the kart, storing it here saves many function calls. */ + float m_kart_length; + + /** Cache width of kart. */ + float m_kart_width; + + /** Keep a pointer to the track to reduce calls */ + Track *m_track; + + /** Keep a pointer to world. */ + LinearWorld *m_world; + + /** A pointer to the AI properties for this kart. */ + const AIProperties *m_ai_properties; + + /** The current node the kart is on. This can be different from the value + * in LinearWorld, since it takes the chosen path of the AI into account + * (e.g. the closest point in LinearWorld might be on a branch not + * chosen by the AI). */ + int m_track_node; + + /** Which of the successors of a node was selected by the AI. */ + std::vector m_successor_index; + /** For each node in the graph this list contains the chosen next node. + * For normal lap track without branches we always have + * m_next_node_index[i] = (i+1) % size; + * but if a branch is possible, the AI will select one option here. + * If the node is not used, m_next_node_index will be -1. */ + std::vector m_next_node_index; + /** For each graph node this list contains a list of the next X + * graph nodes. */ + std::vector > m_all_look_aheads; + + virtual void update (float delta) ; + virtual unsigned int getNextSector(unsigned int index); + virtual void newLap (int lap); + virtual void setControllerName(const std::string &name); + virtual void setSteering (float angle, float dt); + float steerToAngle (const unsigned int sector, const float angle); + float steerToPoint (const Vec3 &point); + float normalizeAngle(float angle); + void computePath(); + virtual bool doSkid(float steer_fraction); + // ------------------------------------------------------------------------ + /** Nothing special to do when the race is finished. */ + virtual void raceFinished() {}; + // ------------------------------------------------------------------------ + /** This can be called to detect if the kart is stuck (i.e. repeatedly + * hitting part of the track). */ + bool isStuck() const { return m_stuck_trigger_rescue; } + + static bool m_ai_debug; +public: + AIBaseLapController(AbstractKart *kart, + StateManager::ActivePlayer *player=NULL); + virtual ~AIBaseLapController() {}; + virtual void reset(); + static void enableDebug() {m_ai_debug = true; } + virtual void crashed(const AbstractKart *k) {}; + virtual void crashed(const Material *m); + virtual void handleZipper(bool play_sound) {}; + virtual void finishedRace(float time) {}; + virtual void collectedItem(const Item &item, int add_info=-1, + float previous_energy=0) {}; + virtual void setPosition(int p) {}; + virtual bool isNetworkController() const { return false; } + virtual bool isPlayerController() const { return false; } + virtual void action(PlayerAction action, int value) {}; + virtual void skidBonusTriggered() {}; + virtual bool disableSlipstreamBonus() const; +}; // AIBaseLapController + +#endif + +/* EOF */ diff --git a/src/karts/controller/ai_properties.hpp b/src/karts/controller/ai_properties.hpp index d8bd0a92c..8f62edf30 100644 --- a/src/karts/controller/ai_properties.hpp +++ b/src/karts/controller/ai_properties.hpp @@ -42,7 +42,7 @@ public: //LEAK_CHECK(); protected: // Give them access to the members - friend class AIBaseController; + friend class AIBaseLapController; friend class SkiddingAI; /** Used to check that all values are defined in the xml file. */ diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index 733b1b8ba..15264c344 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -53,13 +53,13 @@ EndController::EndController(AbstractKart *kart, StateManager::ActivePlayer *player, Controller *prev_controller) - : AIBaseController(kart, player) + : AIBaseLapController(kart, player) { m_previous_controller = prev_controller; if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES && race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER) { - // Overwrite the random selected default path from AIBaseController + // Overwrite the random selected default path from AIBaseLapController // with a path that always picks the first branch (i.e. it follows // the main driveline). std::vector next; @@ -125,7 +125,7 @@ EndController::~EndController() //----------------------------------------------------------------------------- void EndController::reset() { - AIBaseController::reset(); + AIBaseLapController::reset(); m_crash_time = 0.0f; m_time_since_stuck = 0.0f; @@ -180,7 +180,7 @@ void EndController::update(float dt) m_controls->m_brake = false; m_controls->m_accel = 1.0f; - AIBaseController::update(dt); + AIBaseLapController::update(dt); // In case of battle mode: don't do anything if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES || diff --git a/src/karts/controller/end_controller.hpp b/src/karts/controller/end_controller.hpp index c2c09ecab..bbfb8c56c 100644 --- a/src/karts/controller/end_controller.hpp +++ b/src/karts/controller/end_controller.hpp @@ -21,7 +21,7 @@ #ifndef HEADER_END_CONTROLLER_HPP #define HEADER_END_CONTROLLER_HPP -#include "karts/controller/ai_base_controller.hpp" +#include "karts/controller/ai_base_lap_controller.hpp" class Camera; class LinearWorld; @@ -40,7 +40,7 @@ namespace irr /** * \ingroup controller */ -class EndController : public AIBaseController +class EndController : public AIBaseLapController { private: /** Stores the type of the previous controller. This is necessary so that diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index b9b6f9897..e0f3c0786 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -78,7 +78,7 @@ #include SkiddingAI::SkiddingAI(AbstractKart *kart) - : AIBaseController(kart) + : AIBaseLapController(kart) { reset(); // Determine if this AI has superpowers, which happens e.g. @@ -195,7 +195,7 @@ void SkiddingAI::reset() m_skid_probability_state = SKID_PROBAB_NOT_YET; m_last_item_random = NULL; - AIBaseController::reset(); + AIBaseLapController::reset(); m_track_node = QuadGraph::UNKNOWN_SECTOR; QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); if(m_track_node==QuadGraph::UNKNOWN_SECTOR) @@ -207,7 +207,7 @@ void SkiddingAI::reset() m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); } - AIBaseController::reset(); + AIBaseLapController::reset(); } // reset //----------------------------------------------------------------------------- @@ -300,7 +300,7 @@ void SkiddingAI::update(float dt) // The client does not do any AI computations. if(network_manager->getMode()==NetworkManager::NW_CLIENT) { - AIBaseController::update(dt); + AIBaseLapController::update(dt); return; } @@ -308,14 +308,14 @@ void SkiddingAI::update(float dt) if(isStuck() && !m_kart->getKartAnimation()) { new RescueAnimation(m_kart); - AIBaseController::update(dt); + AIBaseLapController::update(dt); return; } if( m_world->isStartPhase() ) { handleRaceStart(); - AIBaseController::update(dt); + AIBaseLapController::update(dt); return; } @@ -390,7 +390,7 @@ void SkiddingAI::update(float dt) } /*And obviously general kart stuff*/ - AIBaseController::update(dt); + AIBaseLapController::update(dt); } // update //----------------------------------------------------------------------------- @@ -2152,7 +2152,7 @@ void SkiddingAI::handleCurve() /** Determines if the kart should skid. The base implementation enables * skidding * \param steer_fraction The steering fraction as computed by the - * AIBaseController. + * AIBaseLapController. * \return True if the kart should skid. */ bool SkiddingAI::doSkid(float steer_fraction) diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index f7d2aacd4..155ff0d61 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -22,7 +22,7 @@ #ifndef HEADER_SKIDDING_AI_HPP #define HEADER_SKIDDING_AI__HPP -#include "karts/controller/ai_base_controller.hpp" +#include "karts/controller/ai_base_lap_controller.hpp" #include "race/race_manager.hpp" #include "tracks/graph_node.hpp" #include "utils/random_generator.hpp" @@ -90,7 +90,7 @@ the AI does the following steps: \ingroup controller */ -class SkiddingAI : public AIBaseController +class SkiddingAI : public AIBaseLapController { private: diff --git a/src/main.cpp b/src/main.cpp index f196dff0b..6ba9231bb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -162,7 +162,7 @@ #include "items/attachment_manager.hpp" #include "items/item_manager.hpp" #include "items/projectile_manager.hpp" -#include "karts/controller/ai_base_controller.hpp" +#include "karts/controller/ai_base_lap_controller.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" #include "modes/demo_world.hpp" @@ -678,7 +678,7 @@ int handleCmdLine(int argc, char **argv) } else if(!strcmp(argv[i], "--ai-debug")) { - AIBaseController::enableDebug(); + AIBaseLapController::enableDebug(); } else if(sscanf(argv[i], "--server=%d",&n)==1) { From 3f9bf2ec47c547a7a91b4160dcb63e8bbc857fc7 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 23 Oct 2013 01:09:18 +0000 Subject: [PATCH 013/170] Merged changes from trunk. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14293 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/CREDITS | Bin 11414 -> 11530 bytes data/gui/soccer_setup.stkgui | 2 +- data/models/materials.xml | 2 +- data/stk_config.xml | 11 + lib/irrlicht/CMakeLists.txt | 2 +- lib/irrlicht/include/IAnimatedMeshSceneNode.h | 8 + lib/irrlicht/include/ISkinnedMesh.h | 2 +- .../Irrlicht/CAnimatedMeshSceneNode.cpp | 19 +- .../source/Irrlicht/CAnimatedMeshSceneNode.h | 10 + lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp | 17 +- lib/irrlicht/source/Irrlicht/CSkinnedMesh.h | 4 +- .../Irrlicht/MacOSX/CIrrDeviceMacOSX.mm | 16 +- sources.cmake | 4 + src/Doxyfile | 2 +- src/Doxyfile_Win | 2 +- src/config/saved_grand_prix.cpp | 5 +- src/graphics/irr_driver.cpp | 4 +- src/graphics/irr_driver.hpp | 2 +- src/graphics/material.cpp | 8 +- src/graphics/material.hpp | 2 +- src/graphics/skid_marks.cpp | 48 ++- src/graphics/skid_marks.hpp | 9 +- src/guiengine/engine.cpp | 10 +- src/guiengine/widgets/list_widget.cpp | 6 +- src/ide/vc10/supertuxkart.vcxproj | 1 + src/ide/vc10/supertuxkart.vcxproj.filters | 63 ++-- src/input/wiimote_manager.cpp | 3 +- src/items/attachment.cpp | 14 +- src/items/bowling.cpp | 23 +- src/items/bowling.hpp | 7 +- src/items/flyable.cpp | 5 + src/items/flyable.hpp | 2 + src/items/powerup.cpp | 4 +- src/items/projectile_manager.cpp | 26 +- src/items/projectile_manager.hpp | 4 +- src/items/rubber_ball.cpp | 23 +- src/items/rubber_band.cpp | 2 +- src/karts/abstract_kart.cpp | 1 + src/karts/abstract_kart.hpp | 6 + src/karts/kart.cpp | 41 ++- src/karts/kart.hpp | 5 +- src/karts/kart_gfx.cpp | 13 +- src/karts/kart_model.cpp | 132 ++++++- src/karts/kart_model.hpp | 45 ++- src/karts/kart_properties.cpp | 11 +- src/karts/kart_properties.hpp | 12 + src/modes/soccer_world.cpp | 52 ++- src/modes/soccer_world.hpp | 25 +- src/modes/world.cpp | 2 +- src/modes/world_status.cpp | 5 +- src/physics/physical_object.cpp | 5 + src/physics/physical_object.hpp | 2 + src/physics/physics.cpp | 14 + src/states_screens/addons_screen.cpp | 4 +- src/states_screens/feature_unlocked.cpp | 2 +- src/states_screens/grand_prix_lose.cpp | 2 +- src/states_screens/grand_prix_win.cpp | 2 +- src/states_screens/kart_selection.cpp | 10 + src/states_screens/race_gui.cpp | 4 +- src/states_screens/race_result_gui.cpp | 323 +++++++++++------- src/states_screens/race_result_gui.hpp | 3 +- src/states_screens/soccer_setup_screen.cpp | 70 +++- src/states_screens/soccer_setup_screen.hpp | 2 + 63 files changed, 894 insertions(+), 271 deletions(-) diff --git a/data/CREDITS b/data/CREDITS index b9d91e226bdc3e81464363cab27134cedb11ff02..cd9fd22eab44f25bcc1677ed287adbc441719dc5 100644 GIT binary patch delta 128 zcmbOh*%h_Hg43Fdfr}xCp_CyB2vZqSfUIPOY@iqygD!&tLoh=DLn2Tlm7$m+gCP^h zQ(!1zC<2RRZ#L$<&L{~|2GUvrRFDXkDghc&!C<(VmAjc)*cYe^WFkm`0#HF7L(1k> HK0!GEd{G(r delta 22 ecmeB*nijdif^%~P=OxC?8QgWuoB#0f$pHXp2?$&O diff --git a/data/gui/soccer_setup.stkgui b/data/gui/soccer_setup.stkgui index b2d58909a..a2084c26c 100644 --- a/data/gui/soccer_setup.stkgui +++ b/data/gui/soccer_setup.stkgui @@ -12,7 +12,7 @@ - + diff --git a/data/models/materials.xml b/data/models/materials.xml index ce6cb4da1..39e9021e4 100644 --- a/data/models/materials.xml +++ b/data/models/materials.xml @@ -11,7 +11,7 @@ - + diff --git a/data/stk_config.xml b/data/stk_config.xml index c42716288..db9ec900a 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -253,6 +253,8 @@ disable-slipstream-usage: even if the AI is not trying to use slipstream, it can get a lot of bonus, esp. on easy since the AI creates trains. Set this to true to make sure AI does not get any slipstream bonus. + shield-incoming-radius: Radius at which projectiles will be detected and + trigger a shield usage. false-start-probability: Probability of a false start. min/max-start-delay: Minimum and maximum start delay. See http://www.humanbenchmark.com/tests/reactiontime/stats.php @@ -316,6 +318,7 @@ straight-length-for-zipper="35" use-slipstream="false" disable-slipstream-usage="true" + shield-incoming-radius="0" false-start-probability="0.08" min-start-delay="0.3" max-start-delay="0.5" nitro-usage="none" @@ -333,6 +336,7 @@ straight-length-for-zipper="35" use-slipstream="false" disable-slipstream-usage="false" + shield-incoming-radius="10" false-start-probability="0.04" min-start-delay="0.25" max-start-delay="0.4" nitro-usage="some" @@ -350,6 +354,7 @@ straight-length-for-zipper="35" use-slipstream="true" disable-slipstream-usage="false" + shield-incoming-radius="10" false-start-probability="0.01" min-start-delay="0.15" max-start-delay="0.28" nitro-usage="all" @@ -367,6 +372,7 @@ straight-length-for-zipper="35" use-slipstream="true" disable-slipstream-usage="false" + shield-incoming-radius="10" false-start-probability="0.0" min-start-delay="0.15" max-start-delay="0.2" nitro-usage="all" @@ -477,6 +483,11 @@ + + + diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index 07d673ac0..14b568e76 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -15,7 +15,7 @@ endif() add_definitions(-DNDEBUG=1 -DIRRLICHT_EXPORTS=1 -DPNG_THREAD_UNSAFE_OK -DPNG_NO_MMX_CODE -DPNG_NO_MNG_FEATURES) if(MSVC) add_definitions(/D_IRR_STATIC_LIB_) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" /MTd) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MTd") else() set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall -pipe -O3 -fno-exceptions -fno-rtti -fstrict-aliasing -fexpensive-optimizations -I/usr/X11R6/include") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pipe -O3 -fno-exceptions -fno-rtti -fstrict-aliasing -fexpensive-optimizations -I/usr/X11R6/include") diff --git a/lib/irrlicht/include/IAnimatedMeshSceneNode.h b/lib/irrlicht/include/IAnimatedMeshSceneNode.h index 84d25a2aa..6cc125cd6 100644 --- a/lib/irrlicht/include/IAnimatedMeshSceneNode.h +++ b/lib/irrlicht/include/IAnimatedMeshSceneNode.h @@ -86,6 +86,14 @@ namespace scene /** \return Frames per second played. */ virtual f32 getAnimationSpeed() const =0; + //! Sets the animation strength (how important the animation is) + /** \param strength: The importance of the animation: 1.f keeps the original animation, 0.f is no animation. */ + virtual void setAnimationStrength(f32 strength) =0; + + //! Gets the animation strength (how important the animation is) + /** \return The importance of the animation: 1.f keeps the original animation, 0.f is no animation. */ + virtual f32 getAnimationStrength() const =0; + //! Creates shadow volume scene node as child of this node. /** The shadow can be rendered using the ZPass or the zfail method. ZPass is a little bit faster because the shadow volume diff --git a/lib/irrlicht/include/ISkinnedMesh.h b/lib/irrlicht/include/ISkinnedMesh.h index 03f5016fb..93dbec801 100644 --- a/lib/irrlicht/include/ISkinnedMesh.h +++ b/lib/irrlicht/include/ISkinnedMesh.h @@ -69,7 +69,7 @@ namespace scene virtual void animateMesh(f32 frame, f32 blend)=0; //! Preforms a software skin on this mesh based of joint positions - virtual void skinMesh() = 0; + virtual void skinMesh(f32 strength=1.f) = 0; //! converts the vertex type of all meshbuffers to tangents. /** E.g. used for bump mapping. */ diff --git a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp index a9f1276be..05dcacb05 100644 --- a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp +++ b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp @@ -33,7 +33,7 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh* mesh, const core::vector3df& scale) : IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0), StartFrame(0), EndFrame(0), FramesPerSecond(0.025f), - CurrentFrameNr(0.f), LastTimeMs(0), + CurrentFrameNr(0.f), AnimationStrength(1.f), LastTimeMs(0), TransitionTime(0), Transiting(0.f), TransitingBlend(0.f), JointMode(EJUOR_NONE), JointsUsed(false), Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false), @@ -210,7 +210,7 @@ IMesh * CAnimatedMeshSceneNode::getMeshForCurrentFrame() skinnedMesh->animateMesh(getFrameNr(), 1.0f); // Update the skinned mesh for the current joint transforms. - skinnedMesh->skinMesh(); + skinnedMesh->skinMesh(AnimationStrength); if (JointMode == EJUOR_READ)//read from mesh { @@ -516,6 +516,21 @@ f32 CAnimatedMeshSceneNode::getAnimationSpeed() const } +//! Sets the animation strength (how important the animation is) +/** \param strength: The importance of the animation: 1.f keeps the original animation, 0.f is no animation. */ +void CAnimatedMeshSceneNode::setAnimationStrength(f32 strength) +{ + AnimationStrength = strength; +} + +//! Gets the animation strength (how important the animation is) +/** \return The importance of the animation: 1.f keeps the original animation, 0.f is no animation. */ +f32 CAnimatedMeshSceneNode::getAnimationStrength() const +{ + return AnimationStrength; +} + + //! returns the axis aligned bounding box of this node const core::aabbox3d& CAnimatedMeshSceneNode::getBoundingBox() const { diff --git a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h index 522393d6e..f2c44f432 100644 --- a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h +++ b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h @@ -66,6 +66,14 @@ namespace scene //! gets the speed with which the animation is played virtual f32 getAnimationSpeed() const; + //! Sets the animation strength (how important the animation is) + /** \param strength: The importance of the animation: 1.f keeps the original animation, 0.f is no animation. */ + virtual void setAnimationStrength(f32 strength); + + //! Gets the animation strength (how important the animation is) + /** \return The importance of the animation: 1.f keeps the original animation, 0.f is no animation. */ + virtual f32 getAnimationStrength() const; + //! returns the material based on the zero based index i. To get the amount //! of materials used by this scene node, use getMaterialCount(). //! This function is needed for inserting the node into the scene hirachy on a @@ -183,6 +191,8 @@ namespace scene f32 FramesPerSecond; f32 CurrentFrameNr; + f32 AnimationStrength; + u32 LastTimeMs; u32 TransitionTime; //Transition time in millisecs f32 Transiting; //is mesh transiting (plus cache of TransitionTime) diff --git a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp index e1ab5a520..bb4a50474 100644 --- a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp +++ b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp @@ -445,7 +445,7 @@ void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint, //-------------------------------------------------------------------------- //! Preforms a software skin on this mesh based of joint positions -void CSkinnedMesh::skinMesh() +void CSkinnedMesh::skinMesh(f32 strength) { if (!HasAnimation || SkinnedLastFrame) return; @@ -478,7 +478,7 @@ void CSkinnedMesh::skinMesh() //skin starting with the root joints for (i=0; isize(); ++i) (*SkinningBuffers)[i]->setDirty(EBT_VERTEX); @@ -486,8 +486,7 @@ void CSkinnedMesh::skinMesh() updateBoundingBox(); } - -void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) +void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint, f32 strength) { if (joint->Weights.size()) { @@ -510,6 +509,14 @@ void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) if (AnimateNormals) jointVertexPull.rotateVect(thisNormalMove, weight.StaticNormal); + // Apply animation strength + if(strength != 1.f) + { + thisVertexMove = core::lerp(weight.StaticPos, thisVertexMove, strength); + if(AnimateNormals) + thisNormalMove = core::lerp(weight.StaticNormal, thisNormalMove, strength); + } + if (! (*(weight.Moved)) ) { *(weight.Moved) = true; @@ -537,7 +544,7 @@ void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) //Skin all children for (u32 j=0; jChildren.size(); ++j) - skinJoint(joint->Children[j], joint); + skinJoint(joint->Children[j], joint, strength); } diff --git a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h index cb3665ca8..564693c84 100644 --- a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h +++ b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h @@ -52,7 +52,7 @@ namespace scene virtual void animateMesh(f32 frame, f32 blend); //! Preforms a software skin on this mesh based of joint positions - virtual void skinMesh(); + virtual void skinMesh(f32 strength=1.f); //! returns amount of mesh buffers. virtual u32 getMeshBufferCount() const; @@ -176,7 +176,7 @@ private: void calculateGlobalMatrices(SJoint *Joint,SJoint *ParentJoint); - void skinJoint(SJoint *Joint, SJoint *ParentJoint); + void skinJoint(SJoint *Joint, SJoint *ParentJoint, f32 strength=1.f); void calculateTangents(core::vector3df& normal, core::vector3df& tangent, core::vector3df& binormal, diff --git a/lib/irrlicht/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm b/lib/irrlicht/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm index 6862c0a5c..4cac8693d 100644 --- a/lib/irrlicht/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm +++ b/lib/irrlicht/source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm @@ -616,8 +616,8 @@ bool CIrrDeviceMacOSX::createWindow() // we need to check where the exceptions may happen and work at them // for now we will just catch them to be able to avoid an app exit - @try - { + //@try + //{ if (!CreationParams.Fullscreen) { if(!CreationParams.WindowId) //create another window when WindowId is null @@ -858,12 +858,12 @@ bool CIrrDeviceMacOSX::createWindow() } } } - } - @catch (NSException *exception) - { - closeDevice(); - result = false; - } + //} + //@catch (NSException *exception) + //{ + // closeDevice(); + // result = false; + //} if (result) { diff --git a/sources.cmake b/sources.cmake index 9e86dc89d..b685237f0 100644 --- a/sources.cmake +++ b/sources.cmake @@ -101,6 +101,8 @@ src/karts/abstract_kart_animation.cpp src/karts/abstract_kart.cpp src/karts/cannon_animation.cpp src/karts/controller/ai_base_controller.cpp +src/karts/controller/ai_base_lap_controller.cpp +src/karts/controller/battle_ai.cpp src/karts/controller/ai_properties.cpp src/karts/controller/controller.cpp src/karts/controller/end_controller.cpp @@ -352,6 +354,8 @@ src/karts/abstract_kart_animation.hpp src/karts/abstract_kart.hpp src/karts/cannon_animation.hpp src/karts/controller/ai_base_controller.hpp +src/karts/controller/ai_base_lap_controller.hpp +src/karts/controller/battle_ai.hpp src/karts/controller/ai_properties.hpp src/karts/controller/controller.hpp src/karts/controller/end_controller.hpp diff --git a/src/Doxyfile b/src/Doxyfile index db6d4679d..e2d368245 100644 --- a/src/Doxyfile +++ b/src/Doxyfile @@ -1139,7 +1139,7 @@ MATHJAX_RELPATH = http://www.mathjax.org/mathjax # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. -SEARCHENGINE = NO +SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client diff --git a/src/Doxyfile_Win b/src/Doxyfile_Win index 6463a9108..72a1af102 100644 --- a/src/Doxyfile_Win +++ b/src/Doxyfile_Win @@ -1202,7 +1202,7 @@ MATHJAX_EXTENSIONS = # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. -SEARCHENGINE = NO +SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client diff --git a/src/config/saved_grand_prix.cpp b/src/config/saved_grand_prix.cpp index 06ed1a188..21d581b0c 100644 --- a/src/config/saved_grand_prix.cpp +++ b/src/config/saved_grand_prix.cpp @@ -19,6 +19,7 @@ #include "config/saved_grand_prix.hpp" +#include "karts/kart_properties_manager.hpp" #include "utils/ptr_vector.hpp" #include "utils/string_utils.hpp" @@ -145,10 +146,12 @@ void SavedGrandPrix::loadKarts(std::vector & kart_list) int aikarts = 0; for(int i = 0; i < m_karts.size(); i++) { + const KartProperties *kp = kart_properties_manager->getKart(m_karts[i].m_ident); + if(m_karts[i].m_local_player_id == -1) { //AI kart found - kart_list[aikarts].m_ident = m_karts[i].m_ident; + if(kp) kart_list[aikarts].m_ident = m_karts[i].m_ident; kart_list[aikarts].m_score = m_karts[i].m_score; kart_list[aikarts].m_overall_time = m_karts[i].m_overall_time; aikarts++; diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 8bb0b230b..5f60c3655 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -994,9 +994,9 @@ void IrrDriver::removeTexture(video::ITexture *t) /** Adds an animated mesh to the scene. * \param mesh The animated mesh to add. */ -scene::IAnimatedMeshSceneNode *IrrDriver::addAnimatedMesh(scene::IAnimatedMesh *mesh) +scene::IAnimatedMeshSceneNode *IrrDriver::addAnimatedMesh(scene::IAnimatedMesh *mesh, scene::ISceneNode* parent) { - return m_scene_manager->addAnimatedMeshSceneNode(mesh, NULL, -1, + return m_scene_manager->addAnimatedMeshSceneNode(mesh, parent, -1, core::vector3df(0,0,0), core::vector3df(0,0,0), core::vector3df(1,1,1), diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 125fa9f60..c43ca9cf4 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -171,7 +171,7 @@ public: void removeMeshFromCache(scene::IMesh *mesh); void removeTexture(video::ITexture *t); scene::IAnimatedMeshSceneNode - *addAnimatedMesh(scene::IAnimatedMesh *mesh); + *addAnimatedMesh(scene::IAnimatedMesh *mesh, scene::ISceneNode* parent=NULL); scene::ICameraSceneNode *addCameraSceneNode(); Camera *addCamera(unsigned int index, AbstractKart *kart); diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 5ef4212b9..18860fd93 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -778,7 +778,6 @@ void Material::install(bool is_full_path, bool complain_if_not_found) { fprintf(stderr, "Applying mask failed for '%s'!\n", m_texname.c_str()); - return; } } m_texture->grab(); @@ -938,8 +937,9 @@ void Material::initParticlesEffect(const XMLNode *node) /** Adjusts the pitch of the given sfx depending on the given speed. * \param sfx The sound effect to adjust. * \param speed The speed of the kart. + * \param should_be_paused Pause for other reasons, i.e. kart is rescued. */ -void Material::setSFXSpeed(SFXBase *sfx, float speed) const +void Material::setSFXSpeed(SFXBase *sfx, float speed, bool should_be_paused) const { // Still make a sound when driving backwards on the material. if (speed < 0) speed = -speed; @@ -947,14 +947,14 @@ void Material::setSFXSpeed(SFXBase *sfx, float speed) const // If we paused it due to too low speed earlier, we can continue now. if (sfx->getStatus() == SFXManager::SFX_PAUSED) { - if (speedplay(); } else if (sfx->getStatus() == SFXManager::SFX_PLAYING) { - if (speedpause(); diff --git a/src/graphics/material.hpp b/src/graphics/material.hpp index 51a6260a0..104375815 100644 --- a/src/graphics/material.hpp +++ b/src/graphics/material.hpp @@ -253,7 +253,7 @@ public: bool complain_if_not_found=true); ~Material (); - void setSFXSpeed(SFXBase *sfx, float speed) const; + void setSFXSpeed(SFXBase *sfx, float speed, bool should_be_paused) const; void setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* mb); void adjustForFog(scene::ISceneNode* parent, video::SMaterial *m, bool use_fog) const; diff --git a/src/graphics/skid_marks.cpp b/src/graphics/skid_marks.cpp index b87d3915a..99f62fe27 100644 --- a/src/graphics/skid_marks.cpp +++ b/src/graphics/skid_marks.cpp @@ -37,10 +37,20 @@ SkidMarks::SkidMarks(const AbstractKart& kart, float width) : m_kart(kart) { m_width = width; m_material = new video::SMaterial(); - m_material->MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; + m_material->MaterialType = video::EMT_ONETEXTURE_BLEND; + m_material->MaterialTypeParam = + pack_textureBlendFunc(video::EBF_SRC_ALPHA, + video::EBF_ONE_MINUS_SRC_ALPHA, + video::EMFN_MODULATE_1X, + video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); m_material->AmbientColor = video::SColor(128, 0, 0, 0); m_material->DiffuseColor = video::SColor(128, 16, 16, 16); + //m_material->AmbientColor = video::SColor(255, 255, 255, 255); + //m_material->DiffuseColor = video::SColor(255, 255, 255, 255); + m_material->setFlag(video::EMF_ANISOTROPIC_FILTER, true); + m_material->setFlag(video::EMF_ZWRITE_ENABLE, false); m_material->Shininess = 0; + m_material->TextureLayer[0].Texture = irr_driver->getTexture("skidmarks.png"); m_skid_marking = false; m_current = -1; } // SkidMark @@ -133,10 +143,19 @@ void SkidMarks::update(float dt, bool force_skid_marks, delta.normalize(); delta *= m_width; + float distance = 0.0f; + Vec3 start = m_left[m_current]->getCenterStart(); + Vec3 newPoint = (raycast_left.m_contactPointWS + raycast_right.m_contactPointWS)/2; + // this linear distance does not account for the kart turning, it's true, + // but it produces good enough results + distance = (newPoint - start).length(); + m_left [m_current]->add(raycast_left.m_contactPointWS, - raycast_left.m_contactPointWS + delta); + raycast_left.m_contactPointWS + delta, + distance); m_right[m_current]->add(raycast_right.m_contactPointWS-delta, - raycast_right.m_contactPointWS); + raycast_right.m_contactPointWS, + distance); // Adjust the boundary box of the mesh to include the // adjusted aabb of its buffers. core::aabbox3df aabb=m_nodes[m_current]->getMesh() @@ -219,6 +238,7 @@ SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left, video::SColor* custom_color) : scene::SMeshBuffer() { + m_center_start = (left + right)/2; m_z_offset = z_offset; m_fade_out = 0.0f; @@ -230,7 +250,7 @@ SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left, Material = *material; m_aabb = core::aabbox3df(left.toIrrVector()); - add(left, right); + add(left, right, 0.0f); } // SkidMarkQuads @@ -240,7 +260,8 @@ SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left, * \param left,right Left and right coordinates. */ void SkidMarks::SkidMarkQuads::add(const Vec3 &left, - const Vec3 &right) + const Vec3 &right, + float distance) { // The skid marks must be raised slightly higher, otherwise it blends // too much with the track. @@ -248,13 +269,25 @@ void SkidMarks::SkidMarkQuads::add(const Vec3 &left, video::S3DVertex v; v.Color = m_start_color; - v.Color.setAlpha(m_start_alpha); + v.Color.setAlpha(0); // initially create all vertices at alpha=0... + + // then when adding a new set of vertices, make the previous 2 opaque. + // this ensures that the last two vertices are always at alpha=0, + // producing a fade-out effect + if (n > 4) + { + Vertices[n - 1].Color.setAlpha(m_start_alpha); + Vertices[n - 2].Color.setAlpha(m_start_alpha); + } + v.Pos = left.toIrrVector(); v.Pos.Y += m_z_offset; v.Normal = core::vector3df(0, 1, 0); + v.TCoords = core::vector2df(0.0f, distance*0.5f); Vertices.push_back(v); v.Pos = right.toIrrVector(); v.Pos.Y += m_z_offset; + v.TCoords = core::vector2df(1.0f, distance*0.5f); Vertices.push_back(v); // Out of the box Irrlicht only supports triangle meshes and not // triangle strips. Since this is a strip it would be more efficient @@ -292,7 +325,8 @@ void SkidMarks::SkidMarkQuads::fade(float f) a -= (a < m_fade_out ? a : (int)m_fade_out); c.setAlpha(a); - for(unsigned int i=0; igetFrameSize().Width; - const float normal_text_scale = - 0.7f + 0.2f*std::max(0, screen_width - 640)/564.0f; - const float title_text_scale = - 0.2f + 0.2f*std::max(0, screen_width - 640)/564.0f; + const int screen_height = irr_driver->getFrameSize().Height; + float scale = std::max(0, screen_width - 640)/564.0f; + if (screen_height < 700) scale = std::min(scale, 0.25f); // attempt to compensate for small screens + + float normal_text_scale = 0.7f + 0.2f*scale; + float title_text_scale = 0.2f + 0.2f*scale; ScalableFont* sfont = new ScalableFont(g_env, diff --git a/src/guiengine/widgets/list_widget.cpp b/src/guiengine/widgets/list_widget.cpp index df0acff87..04ef0d741 100644 --- a/src/guiengine/widgets/list_widget.cpp +++ b/src/guiengine/widgets/list_widget.cpp @@ -38,7 +38,7 @@ ListWidget::ListWidget() : Widget(WTYPE_LIST) m_icons = NULL; m_listener = NULL; m_selected_column = NULL; - m_sort_desc = true; + m_sort_desc = false; m_sort_default = true; } @@ -300,7 +300,7 @@ void ListWidget::elementRemoved() } m_header_elements.clearAndDeleteAll(); m_selected_column = NULL; - m_sort_desc = true; + m_sort_desc = false; m_sort_default = true; } @@ -378,8 +378,8 @@ EventPropagation ListWidget::transmitEvent(Widget* w, } else { - m_sort_default = m_sort_desc && !m_sort_default; if (!m_sort_default) m_sort_desc = !m_sort_desc; + m_sort_default = !m_sort_desc && !m_sort_default; } m_sort_col = originator[(m_properties[PROP_ID] + "_column_").size()] - '0'; diff --git a/src/ide/vc10/supertuxkart.vcxproj b/src/ide/vc10/supertuxkart.vcxproj index a4c176d7d..e3cdeabda 100644 --- a/src/ide/vc10/supertuxkart.vcxproj +++ b/src/ide/vc10/supertuxkart.vcxproj @@ -231,6 +231,7 @@ + diff --git a/src/ide/vc10/supertuxkart.vcxproj.filters b/src/ide/vc10/supertuxkart.vcxproj.filters index dbaafeb09..9d8e164d0 100644 --- a/src/ide/vc10/supertuxkart.vcxproj.filters +++ b/src/ide/vc10/supertuxkart.vcxproj.filters @@ -35,12 +35,6 @@ {1298df87-da0e-462e-b074-a496598571d8} - - {11a7d573-5959-4be8-8a3f-d455d7833333} - - - {d477b64b-eb9c-4059-a647-98f3a2f7203a} - {2f186570-85e9-4cea-88dc-0f716c6e141a} @@ -154,6 +148,12 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + {11a7d573-5959-4be8-8a3f-d455d7833333} + + + {d477b64b-eb9c-4059-a647-98f3a2f7203a} + @@ -885,6 +885,9 @@ Source Files\karts\controller + + Source Files\karts\controller + @@ -1158,34 +1161,34 @@ Header Files\physics - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts\controller + Header Files\items\karts\controller - Header Files\karts\controller + Header Files\items\karts\controller - Header Files\karts\controller + Header Files\items\karts\controller Header Files\graphics @@ -1539,7 +1542,7 @@ Header Files\graphics - Header Files\karts + Header Files\items\karts Header Files\tracks @@ -1575,7 +1578,7 @@ Header Files\states_screens - Header Files\karts + Header Files\items\karts Header Files\states_screens\dialogs @@ -1590,28 +1593,28 @@ Header Files\replay - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts - Header Files\karts + Header Files\items\karts Header Files\modes @@ -1626,7 +1629,7 @@ Header Files\graphics - Header Files\karts\controller + Header Files\items\karts\controller Header Files\modes @@ -1638,7 +1641,7 @@ Header Files\addons - Header Files\karts\controller + Header Files\items\karts\controller Header Files\utils @@ -1689,10 +1692,10 @@ Header Files\tracks - Header Files\karts\controller + Header Files\items\karts\controller - Header Files\karts\controller + Header Files\items\karts\controller \ No newline at end of file diff --git a/src/input/wiimote_manager.cpp b/src/input/wiimote_manager.cpp index 6300f8c26..5ac93aed4 100644 --- a/src/input/wiimote_manager.cpp +++ b/src/input/wiimote_manager.cpp @@ -306,7 +306,8 @@ int WiimoteManager::askUserToConnectWiimotes() { new MessageDialog( _("Press the buttons 1+2 simultaneously on your wiimote to put " - "it in discovery mode, then click on OK."), + "it in discovery mode, then click on Yes." + "Detailed instructions at supertuxkart.net/Wiimote"), MessageDialog::MESSAGE_DIALOG_CONFIRM, new WiimoteDialogListener(), true); diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index eafea9d8c..f342c31ff 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -329,9 +329,15 @@ void Attachment::hitBanana(Item *item, int new_attachment) void Attachment::handleCollisionWithKart(AbstractKart *other) { Attachment *attachment_other=other->getAttachment(); - + if(getType()==Attachment::ATTACH_BOMB) { + // Don't attach a bomb when the kart is shielded + if(other->isShielded()) + { + other->decreaseShieldTime(); + return; + } // If both karts have a bomb, explode them immediately: if(attachment_other->getType()==Attachment::ATTACH_BOMB) { @@ -356,6 +362,12 @@ void Attachment::handleCollisionWithKart(AbstractKart *other) else if(attachment_other->getType()==Attachment::ATTACH_BOMB && (attachment_other->getPreviousOwner()!=m_kart || World::getWorld()->getNumKarts() <= 2)) { + // Don't attach a bomb when the kart is shielded + if(m_kart->isShielded()) + { + m_kart->decreaseShieldTime(); + return; + } set(ATTACH_BOMB, other->getAttachment()->getTimeLeft()+ stk_config->m_bomb_time_increase, other); other->getAttachment()->clear(); diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index 567f0b976..bb1817a91 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -18,6 +18,8 @@ #include "items/bowling.hpp" +#include "audio/sfx_base.hpp" +#include "audio/sfx_manager.hpp" #include "graphics/hit_sfx.hpp" #include "graphics/material.hpp" #include "io/xml_node.hpp" @@ -74,8 +76,22 @@ Bowling::Bowling(AbstractKart *kart) // should not live forever, auto-destruct after 20 seconds m_max_lifespan = 20; + m_roll_sfx = sfx_manager->createSoundSource("bowling_roll"); + m_roll_sfx->play(); + m_roll_sfx->setLoop(true); + } // Bowling +// ---------------------------------------------------------------------------- +/** Destructor, removes any playing sfx. + */ +Bowling::~Bowling() +{ + if(m_roll_sfx->getStatus()==SFXManager::SFX_PLAYING) + m_roll_sfx->stop(); + sfx_manager->deleteSFX(m_roll_sfx); +} // ~RubberBall + // ----------------------------------------------------------------------------- /** Initialises this object with data from the power.xml file. * \param node XML Node @@ -200,6 +216,10 @@ bool Bowling::updateAndDelete(float dt) hit(NULL); return true; } + + if (m_roll_sfx->getStatus()==SFXManager::SFX_PLAYING) + m_roll_sfx->position(getXYZ()); + return false; } // updateAndDelete // ----------------------------------------------------------------------------- @@ -237,5 +257,6 @@ HitEffect* Bowling::getHitEffect() const { if(m_has_hit_kart) return new HitSFX(getXYZ(), "strike"); - return NULL; + else + return new HitSFX(getXYZ(), "crash"); } // getHitEffect diff --git a/src/items/bowling.hpp b/src/items/bowling.hpp index 9841ee0f7..f8ef002c6 100644 --- a/src/items/bowling.hpp +++ b/src/items/bowling.hpp @@ -29,6 +29,7 @@ using namespace irr; #include "items/flyable.hpp" class XMLNode; +class SFXBase; /** * \ingroup items @@ -45,8 +46,12 @@ private: * kart was hit. */ bool m_has_hit_kart; + /** A sound effect for rolling ball. */ + SFXBase *m_roll_sfx; + public: - Bowling(AbstractKart* kart); + Bowling(AbstractKart* kart); + virtual ~Bowling(); static void init(const XMLNode &node, scene::IMesh *bowling); virtual bool updateAndDelete(float dt); virtual const core::stringw getHitString(const AbstractKart *kart) const; diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index 2655a4d09..ed35ac2af 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -515,4 +515,9 @@ HitEffect* Flyable::getHitEffect() const return new Explosion(getXYZ(), "explosion", "explosion_cake.xml"); } // getHitEffect +// ---------------------------------------------------------------------------- +unsigned int Flyable::getOwnerId() +{ + return m_owner->getWorldKartId(); +} /* EOF */ diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index bfd46835f..b10960aa9 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -208,6 +208,8 @@ public: * call, or if the inheriting object will update TerrainInfo itself * (or perhaps not at all if it is not needed). */ void setDoTerrainInfo(bool d) { m_do_terrain_info = d; } + // ------------------------------------------------------------------------ + unsigned int getOwnerId(); }; // Flyable #endif diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 1f02d270d..a19ade9e0 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -152,7 +152,7 @@ void Powerup::set(PowerupManager::PowerupType type, int n) break ; case PowerupManager::POWERUP_BOWLING: - m_sound_use = sfx_manager->createSoundSource("bowling_roll"); + m_sound_use = sfx_manager->createSoundSource("bowling_shoot"); break ; case PowerupManager::POWERUP_ANVIL: @@ -264,7 +264,7 @@ void Powerup::use() Powerup::adjustSound(); m_sound_use->play(); - projectile_manager->newProjectile(m_owner, world->getTrack(), m_type); + projectile_manager->newProjectile(m_owner, m_type); break ; case PowerupManager::POWERUP_SWATTER: diff --git a/src/items/projectile_manager.cpp b/src/items/projectile_manager.cpp index f7846b684..ce96adb6e 100644 --- a/src/items/projectile_manager.cpp +++ b/src/items/projectile_manager.cpp @@ -156,8 +156,13 @@ void ProjectileManager::updateClient(float dt) } // for i in m_active_projectiles } // updateClient + // ----------------------------------------------------------------------------- -Flyable *ProjectileManager::newProjectile(AbstractKart *kart, Track* track, +/** Creates a new projectile of the given type. + * \param kart The kart which shoots the projectile. + * \param type Type of projectile. + */ +Flyable *ProjectileManager::newProjectile(AbstractKart *kart, PowerupManager::PowerupType type) { Flyable *f; @@ -173,3 +178,22 @@ Flyable *ProjectileManager::newProjectile(AbstractKart *kart, Track* track, m_active_projectiles.push_back(f); return f; } // newProjectile +// ----------------------------------------------------------------------------- +/** Returns true if a projectile is within the given distance of the specified + * kart. + * \param kart The kart for which the test is done. + * \param radius Distance within which the projectile must be. +*/ +bool ProjectileManager::projectileIsClose(const AbstractKart * const kart, + float radius) +{ + float r2 = radius*radius; + + for(Projectiles::iterator i = m_active_projectiles.begin(); + i != m_active_projectiles.end(); i++) + { + float dist2 = (*i)->getXYZ().distance2(kart->getXYZ()); + if(dist2(World::getWorld()); + // FIXME: what does the rubber ball do in case of battle mode?? + if(!world) return; + computeTarget(); // initialises the current graph node @@ -141,8 +145,6 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz) void RubberBall::computeTarget() { LinearWorld *world = dynamic_cast(World::getWorld()); - // FIXME: what does the rubber ball do in case of battle mode?? - if(!world) return; for(unsigned int p = race_manager->getFinishedKarts()+1; p < world->getNumKarts()+1; p++) @@ -190,13 +192,11 @@ unsigned int RubberBall::getSuccessorToHitTarget(unsigned int node_index, { int succ = 0; LinearWorld *lin_world = dynamic_cast(World::getWorld()); - // FIXME: what does the rubber ball do in case of battle mode?? - if(lin_world) - { - unsigned int sect = - lin_world->getSectorForKart(m_target); - succ = QuadGraph::get()->getNode(node_index).getSuccessorToReach(sect); - } + + unsigned int sect = + lin_world->getSectorForKart(m_target); + succ = QuadGraph::get()->getNode(node_index).getSuccessorToReach(sect); + if(dist) *dist += QuadGraph::get()->getNode(node_index) .getDistanceToSuccessor(succ); @@ -322,6 +322,10 @@ const core::stringw RubberBall::getHitString(const AbstractKart *kart) const */ bool RubberBall::updateAndDelete(float dt) { + LinearWorld *world = dynamic_cast(World::getWorld()); + // FIXME: what does the rubber ball do in case of battle mode?? + if(!world) return true; + if(m_delete_timer>0) { m_delete_timer -= dt; @@ -629,7 +633,6 @@ float RubberBall::getMaxTerrainHeight(const Vec3 &vertical_offset) const void RubberBall::updateDistanceToTarget() { const LinearWorld *world = dynamic_cast(World::getWorld()); - if(!world) return; // FIXME battle mode float target_distance = world->getDistanceDownTrackForKart(m_target->getWorldKartId()); diff --git a/src/items/rubber_band.cpp b/src/items/rubber_band.cpp index 7fe347d68..538708010 100644 --- a/src/items/rubber_band.cpp +++ b/src/items/rubber_band.cpp @@ -137,7 +137,7 @@ void RubberBand::updatePosition() */ void RubberBand::update(float dt) { - if(m_owner->isEliminated() || m_owner->isShielded()) + if(m_owner->isEliminated()) { // Rubber band snaps m_plunger->hit(NULL); diff --git a/src/karts/abstract_kart.cpp b/src/karts/abstract_kart.cpp index 656c422f1..f84ed3b6f 100644 --- a/src/karts/abstract_kart.cpp +++ b/src/karts/abstract_kart.cpp @@ -55,6 +55,7 @@ AbstractKart::AbstractKart(const std::string& ident, m_kart_length = m_kart_model->getLength(); m_wheel_graphics_position = m_kart_model->getWheelsGraphicsPosition(); m_nitro_emitter_position = m_kart_model->getNitroEmittersPositon(); + m_has_nitro_emitter = m_kart_model->hasNitroEmitters(); } // AbstractKart // ---------------------------------------------------------------------------- diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 1fc3d3031..75a1c283d 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -55,6 +55,8 @@ private: const Vec3* m_wheel_graphics_position; /** The position of all nitro emitters in the 3d model */ const Vec3* m_nitro_emitter_position; + /** True if kart has nitro emitters */ + bool m_has_nitro_emitter; /** Index of kart in world. */ unsigned int m_world_kart_id; @@ -161,6 +163,10 @@ public: /** Returns the position of a nitro emitter relative to the kart */ const Vec3& getNitroEmitterPosition(int i) const {assert(i>=0 && i<2); return m_nitro_emitter_position[i];} + // ------------------------------------------------------------------------ + /** Returns true if kart has nitro emitters */ + const bool hasNitroEmitter() const + {return m_has_nitro_emitter;} // ======================================================================== // Emergency animation related functions. diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 1ada088eb..64c33ed14 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -157,6 +157,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_engine_sound = sfx_manager->createSoundSource(m_kart_properties->getEngineSfxType()); m_beep_sound = sfx_manager->createSoundSource( "horn" ); m_crash_sound = sfx_manager->createSoundSource( "crash" ); + m_boing_sound = sfx_manager->createSoundSource( "boing" ); m_goo_sound = sfx_manager->createSoundSource( "goo" ); m_skid_sound = sfx_manager->createSoundSource( "skid" ); m_terrain_sound = NULL; @@ -181,6 +182,7 @@ void Kart::init(RaceManager::KartType type) m_goo_sound->volume( 1.0f / factor ); m_skid_sound->volume( 1.0f / factor ); m_crash_sound->volume( 1.0f / factor ); + m_boing_sound->volume( 1.0f / factor ); m_beep_sound->volume( 1.0f / factor ); } else @@ -189,6 +191,7 @@ void Kart::init(RaceManager::KartType type) m_skid_sound->volume( 1.0f / race_manager->getNumberOfKarts() ); m_crash_sound->volume( 1.0f / race_manager->getNumberOfKarts() ); m_beep_sound->volume( 1.0f / race_manager->getNumberOfKarts() ); + m_boing_sound->volume( 1.0f / race_manager->getNumberOfKarts() ); } } @@ -244,6 +247,7 @@ Kart::~Kart() sfx_manager->deleteSFX(m_skid_sound ); sfx_manager->deleteSFX(m_goo_sound ); sfx_manager->deleteSFX(m_beep_sound ); + sfx_manager->deleteSFX(m_boing_sound ); delete m_kart_gfx; if(m_terrain_sound) sfx_manager->deleteSFX(m_terrain_sound); if(m_previous_terrain_sound) sfx_manager->deleteSFX(m_previous_terrain_sound); @@ -1032,6 +1036,7 @@ void Kart::eliminate() m_stars_effect->update(1); } + m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0); m_eliminated = true; m_node->setVisible(false); @@ -1171,6 +1176,7 @@ void Kart::update(float dt) m_engine_sound->position ( getXYZ() ); m_crash_sound->position ( getXYZ() ); m_skid_sound->position ( getXYZ() ); + m_boing_sound->position ( getXYZ() ); // Check if a kart is (nearly) upside down and not moving much --> automatic rescue if(World::getWorld()->getTrack()->isAutoRescueEnabled() && @@ -1405,6 +1411,7 @@ void Kart::handleMaterialSFX(const Material *material) m_terrain_sound = NULL; } } + if(m_previous_terrain_sound && m_previous_terrain_sound->getStatus()==SFXManager::SFX_STOPPED) { @@ -1414,15 +1421,19 @@ void Kart::handleMaterialSFX(const Material *material) sfx_manager->deleteSFX(m_previous_terrain_sound); m_previous_terrain_sound = NULL; } + + bool m_schedule_pause = m_flying || + dynamic_cast(getKartAnimation()) || + dynamic_cast(getKartAnimation()); // terrain sound is not necessarily a looping sound so check its status before // setting its speed, to avoid 'ressuscitating' sounds that had already stopped - if(m_terrain_sound - && (m_terrain_sound->getStatus() == SFXManager::SFX_PLAYING - || m_terrain_sound->getStatus() == SFXManager::SFX_PAUSED)) + if(m_terrain_sound && + (m_terrain_sound->getStatus()==SFXManager::SFX_PLAYING || + m_terrain_sound->getStatus()==SFXManager::SFX_PAUSED)) { m_terrain_sound->position(getXYZ()); - material->setSFXSpeed(m_terrain_sound, m_speed); + material->setSFXSpeed(m_terrain_sound, m_speed, m_schedule_pause); } } // handleMaterialSFX @@ -1668,7 +1679,7 @@ void Kart::crashed(AbstractKart *k, bool update_attachments) getAttachment()->handleCollisionWithKart(k); } m_controller->crashed(k); - crashed(); + crashed(NULL, k); } // crashed(Kart, update_attachments // ----------------------------------------------------------------------------- @@ -1808,13 +1819,15 @@ void Kart::crashed(const Material *m, const Vec3 &normal) } // if(m && m->getCollisionReaction() != Material::NORMAL && // !getKartAnimation()) m_controller->crashed(m); - crashed(); + crashed(m, NULL); } // crashed(Material) // ----------------------------------------------------------------------------- /** Common code used when a kart or a material was hit. + * @param m The material collided into, or NULL if none + * @param k The kart collided into, or NULL if none */ -void Kart::crashed() +void Kart::crashed(const Material* m, AbstractKart *k) { if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return; @@ -1828,8 +1841,16 @@ void Kart::crashed() { // In case that the sfx is longer than 0.5 seconds, only play it if // it's not already playing. - if(m_crash_sound->getStatus() != SFXManager::SFX_PLAYING) - m_crash_sound->play(); + if (isShielded() || (k != NULL && k->isShielded())) + { + if (m_boing_sound->getStatus() != SFXManager::SFX_PLAYING) + m_boing_sound->play(); + } + else + { + if(m_crash_sound->getStatus() != SFXManager::SFX_PLAYING) + m_crash_sound->play(); + } } m_bounce_back_time = 0.1f; @@ -2368,7 +2389,7 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, wheel_up_axis[i] = m_default_suspension_length[i] - m_vehicle->getWheelInfo(i).m_raycastInfo.m_suspensionLength; } - m_kart_model->update(m_wheel_rotation_dt, getSteerPercent(), wheel_up_axis); + m_kart_model->update(m_wheel_rotation_dt, getSteerPercent(), wheel_up_axis, m_speed); Vec3 center_shift = m_kart_properties->getGravityCenterShift(); float y = m_vehicle->getWheelInfo(0).m_chassisConnectionPointCS.getY() diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 960120fc8..1cb11fdc9 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -130,7 +130,7 @@ private: /** Current leaning of the kart. */ float m_current_lean; - /** If > 0 then bubble gum effect is on */ + /** If > 0 then bubble gum effect is on. This is the sliding when hitting a gum on the floor, not the shield. */ float m_bubblegum_time; /** The torque to apply after hitting a bubble gum. */ @@ -211,6 +211,7 @@ private: SFXBase *m_previous_terrain_sound; SFXBase *m_skid_sound; SFXBase *m_goo_sound; + SFXBase *m_boing_sound; float m_time_last_crash; /** To prevent using nitro in too short bursts */ @@ -225,7 +226,7 @@ private: void updateEngineSFX(); void updateNitro(float dt); float getActualWheelForce(); - void crashed(); + void crashed(const Material* m, AbstractKart *k); void loadData(RaceManager::KartType type, bool animatedModel); public: diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index cf2906ffc..36e479665 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -50,13 +50,16 @@ KartGFX::KartGFX(const AbstractKart *kart) */ - Vec3 rear_left(kart->getWheelGraphicsPosition(3).toIrrVector().X, (kart->getKartHeight()-0.6f)*0.35f, - kart->getWheelGraphicsPosition(3).toIrrVector().Z); - Vec3 rear_right(kart->getWheelGraphicsPosition(2).toIrrVector().X, (kart->getKartHeight()-0.6f)*0.35f, - kart->getWheelGraphicsPosition(2).toIrrVector().Z); + Vec3 rear_left(kart->getWheelGraphicsPosition(3).toIrrVector().X, 0.05f, + kart->getWheelGraphicsPosition(3).toIrrVector().Z-0.1f); + Vec3 rear_right(kart->getWheelGraphicsPosition(2).toIrrVector().X, 0.05f, + kart->getWheelGraphicsPosition(2).toIrrVector().Z-0.1f); Vec3 rear_center(0, kart->getKartHeight()*0.35f, -kart->getKartLength()*0.35f); + + Vec3 rear_nitro_center(0, kart->getKartHeight()*0.2f, + -kart->getKartLength()*0.1f); // FIXME Used to match the emitter as seen in blender const float delta = 0.6f; @@ -67,6 +70,8 @@ KartGFX::KartGFX(const AbstractKart *kart) Vec3 rear_nitro_left(kart->getNitroEmitterPosition(1).toIrrVector().X, kart->getNitroEmitterPosition(1).toIrrVector().Y, kart->getNitroEmitterPosition(1).toIrrVector().Z + delta); + if (!kart->hasNitroEmitter()) + rear_nitro_right = rear_nitro_left = rear_nitro_center; // Create all effects. Note that they must be created // in the order of KartGFXType. diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 730dbb549..406a0da6b 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -89,6 +89,7 @@ KartModel::KartModel(bool is_master) m_wheel_filename[1] = ""; m_wheel_filename[2] = ""; m_wheel_filename[3] = ""; + m_speed_weighted_objects.clear(); m_animated_node = NULL; m_mesh = NULL; for(unsigned int i=AF_BEGIN; i<=AF_END; i++) @@ -123,6 +124,8 @@ void KartModel::loadInfo(const XMLNode &node) animation_node->get("start-jump", &m_animation_frame[AF_JUMP_START]); animation_node->get("start-jump-loop",&m_animation_frame[AF_JUMP_LOOP] ); animation_node->get("end-jump", &m_animation_frame[AF_JUMP_END] ); + animation_node->get("start-speed-weighted", &m_animation_frame[AF_SPEED_WEIGHTED_START] ); + animation_node->get("end-speed-weighted", &m_animation_frame[AF_SPEED_WEIGHTED_END] ); animation_node->get("speed", &m_animation_speed ); } @@ -135,13 +138,22 @@ void KartModel::loadInfo(const XMLNode &node) } m_nitro_emitter_position[0] = Vec3 (0,0.1f,0); - m_nitro_emitter_position[1] = Vec3 (0,0.1f,0); + m_has_nitro_emitter = false; if(const XMLNode *nitroEmitter_node=node.getNode("nitro-emitter")) { loadNitroEmitterInfo(*nitroEmitter_node, "nitro-emitter-a", 0); loadNitroEmitterInfo(*nitroEmitter_node, "nitro-emitter-b", 1); + m_has_nitro_emitter = true; + } + + if(const XMLNode *speedWeighted_node=node.getNode("speed-weighted")) + { + for(unsigned int i=0 ; i < speedWeighted_node->getNumNodes() ; i++) + { + loadSpeedWeightedInfo(speedWeighted_node->getNode(i)); + } } if(const XMLNode *hat_node=node.getNode("hat")) @@ -184,6 +196,22 @@ KartModel::~KartModel() } } + for(size_t i=0; idrop(); + } + if(m_is_master && m_speed_weighted_objects[i].m_model) + { + irr_driver->dropAllTextures(m_speed_weighted_objects[i].m_model); + irr_driver->removeMeshFromCache(m_speed_weighted_objects[i].m_model); + } + } + if(m_is_master && m_mesh) { m_mesh->drop(); @@ -231,6 +259,7 @@ KartModel* KartModel::makeCopy() km->m_nitro_emitter_position[0] = m_nitro_emitter_position[0]; km->m_nitro_emitter_position[1] = m_nitro_emitter_position[1]; + km->m_has_nitro_emitter = m_has_nitro_emitter; for(unsigned int i=0; i<4; i++) { @@ -245,6 +274,17 @@ KartModel* KartModel::makeCopy() km->m_max_suspension[i] = m_max_suspension[i]; km->m_dampen_suspension_amplitude[i]= m_dampen_suspension_amplitude[i]; } + + km->m_speed_weighted_objects.resize(m_speed_weighted_objects.size()); + for(size_t i=0; im_speed_weighted_objects[i].m_model = m_speed_weighted_objects[i].m_model; + // Master should not have any speed weighted nodes. + assert(!m_speed_weighted_objects[i].m_node); + km->m_speed_weighted_objects[i].m_name = m_speed_weighted_objects[i].m_name; + km->m_speed_weighted_objects[i].m_position = m_speed_weighted_objects[i].m_position; + } + for(unsigned int i=AF_BEGIN; i<=AF_END; i++) km->m_animation_frame[i] = m_animation_frame[i]; @@ -297,6 +337,13 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) if(!m_wheel_model[i]) continue; m_wheel_node[i]->setParent(lod_node); } + + // Become the owner of the speed weighted objects + for(size_t i=0; isetParent(lod_node); + } } else { @@ -314,6 +361,8 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) std::string debug_name = m_model_filename+" (kart-model)"; node->setName(debug_name.c_str()); #endif + + // Attach the wheels for(unsigned int i=0; i<4; i++) { if(!m_wheel_model[i]) continue; @@ -325,6 +374,27 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) #endif m_wheel_node[i]->setPosition(m_wheel_graphics_position[i].toIrrVector()); } + + // Attach the speed weighted objects + set the animation state + for(size_t i=0 ; i < m_speed_weighted_objects.size() ; i++) + { + SpeedWeightedObject& obj = m_speed_weighted_objects[i]; + obj.m_node = NULL; + if(obj.m_model) + { + obj.m_node = irr_driver->addAnimatedMesh(obj.m_model, node); + obj.m_node->grab(); + + obj.m_node->setAnimationStrength(0.0f); + obj.m_node->setFrameLoop(m_animation_frame[AF_SPEED_WEIGHTED_START], m_animation_frame[AF_SPEED_WEIGHTED_END]); + + #ifdef DEBUG + std::string debug_name = obj.m_name+" (speed-weighted)"; + obj.m_node->setName(debug_name.c_str()); + #endif + obj.m_node->setPosition(obj.m_position.toIrrVector()); + } + } } return node; } // attachModel @@ -348,10 +418,31 @@ bool KartModel::loadModels(const KartProperties &kart_properties) m_mesh->grab(); irr_driver->grabAllTextures(m_mesh); - Vec3 min, max; - MeshTools::minMax3D(m_mesh->getMesh(m_animation_frame[AF_STRAIGHT]), &min, &max); + Vec3 kart_min, kart_max; + MeshTools::minMax3D(m_mesh->getMesh(m_animation_frame[AF_STRAIGHT]), &kart_min, &kart_max); - Vec3 size = max-min; + // Load the speed weighted object models. We need to do that now because it can affect the dimensions of the kart + for(size_t i=0 ; i < m_speed_weighted_objects.size() ; i++) + { + SpeedWeightedObject& obj = m_speed_weighted_objects[i]; + std::string full_name = + kart_properties.getKartDir()+obj.m_name; + obj.m_model = irr_driver->getAnimatedMesh(full_name); + // Grab all textures. This is done for the master only, so + // the destructor will only free the textures if a master + // copy is freed. + irr_driver->grabAllTextures(obj.m_model); + + // Update min/max + Vec3 obj_min, obj_max; + MeshTools::minMax3D(obj.m_model, &obj_min, &obj_max); + obj_min += obj.m_position; + obj_max += obj.m_position; + kart_min.min(obj_min); + kart_max.min(obj_max); + } + + Vec3 size = kart_max-kart_min; m_kart_width = size.getX(); m_kart_height = size.getY(); m_kart_length = size.getZ(); @@ -416,6 +507,18 @@ void KartModel::loadNitroEmitterInfo(const XMLNode &node, emitter_node->get("position", &m_nitro_emitter_position[index]); } // loadNitroEmitterInfo +/** Loads a single speed weighted node. */ +void KartModel::loadSpeedWeightedInfo(const XMLNode* speed_weighted_node) +{ + SpeedWeightedObject obj; + + speed_weighted_node->get("position", &obj.m_position); + speed_weighted_node->get("model", &obj.m_name); + + if(!obj.m_name.empty()) + m_speed_weighted_objects.push_back(obj); +} + // ---------------------------------------------------------------------------- /** Loads a single wheel node. Currently this is the name of the wheel model * and the position of the wheel relative to the kart. @@ -486,7 +589,7 @@ void KartModel::reset() { // Reset the wheels const float suspension[4]={0,0,0,0}; - update(0, 0.0f, suspension); + update(0, 0.0f, suspension, 0.0f); // Stop any animations currently being played. setAnimation(KartModel::AF_DEFAULT); @@ -592,12 +695,14 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node) } // OnAnimationEnd // ---------------------------------------------------------------------------- -/** Rotates and turns the wheels appropriately, and adjust for suspension. +/** Rotates and turns the wheels appropriately, and adjust for suspension + + updates the speed-weighted objects' animations. * \param rotation_dt How far the wheels have rotated since last time. * \param steer The actual steer settings. * \param suspension Suspension height for all four wheels. + * \param speed The speed of the kart in meters/sec, used for the speed-weighted objects' animations */ -void KartModel::update(float rotation_dt, float steer, const float suspension[4]) +void KartModel::update(float rotation_dt, float steer, const float suspension[4], float speed) { float clamped_suspension[4]; // Clamp suspension to minimum and maximum suspension length, so that @@ -653,6 +758,18 @@ void KartModel::update(float rotation_dt, float steer, const float suspension[4] // If animations are disabled, stop here if (m_animated_node == NULL) return; + // Update the speed-weighted objects' animations + for(size_t i=0 ; i < m_speed_weighted_objects.size() ; i++) + { + SpeedWeightedObject& obj = m_speed_weighted_objects[i]; + float strength = speed * m_kart->getKartProperties()->getSpeedWeightedStrengthFactor(); + btClamp(strength, 0.0f, 1.0f); + obj.m_node->setAnimationStrength(strength); + + float anim_speed = speed * m_kart->getKartProperties()->getSpeedWeightedSpeedFactor(); + obj.m_node->setAnimationSpeed(anim_speed); + } + // Check if the end animation is being played, if so, don't // play steering animation. if(m_current_animation!=AF_DEFAULT) return; @@ -708,4 +825,3 @@ void KartModel::attachHat(){ } // if bone } // if(m_hat_name) } - diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index 9fdb3eea9..17717f9aa 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -20,6 +20,7 @@ #define HEADER_KART_MODEL_HPP #include +#include #include namespace irr @@ -36,6 +37,23 @@ class AbstractKart; class KartProperties; class XMLNode; +struct SpeedWeightedObject +{ + SpeedWeightedObject() : m_model(NULL), m_node(NULL), m_position(), m_name() {} + /** Model */ + scene::IAnimatedMesh * m_model; + + /** The scene node the speed weighted model is attached to */ + scene::IAnimatedMeshSceneNode * m_node; + + /** The position of the "speed weighted" objects relative to the kart */ + Vec3 m_position; + + /** Filename of the "speed weighted" object */ + std::string m_name; +}; +typedef std::vector SpeedWeightedObjectList; + /** * \brief This class stores a 3D kart model. * It takes especially care of attaching @@ -66,7 +84,9 @@ public: AF_WIN_START, // Begin of win animation AF_WIN_LOOP_START, // Begin of win loop animation AF_WIN_END, // End of win animation - AF_END=AF_WIN_END, // Last animation frame + AF_SPEED_WEIGHTED_START, // Start of speed-weighted animation + AF_SPEED_WEIGHTED_END, // End of speed-weighted animation + AF_END=AF_SPEED_WEIGHTED_END, // Last animation frame AF_COUNT}; // Number of entries here private: /** Which frame number starts/end which animation. */ @@ -120,6 +140,12 @@ private: /** The position of the nitro emitters */ Vec3 m_nitro_emitter_position[2]; + /** True if kart has nitro emitters */ + bool m_has_nitro_emitter; + + /** The speed weighted objects. */ + SpeedWeightedObjectList m_speed_weighted_objects; + /** Minimum suspension length. If the displayed suspension is * shorter than this, the wheel would look wrong. */ float m_min_suspension[4]; @@ -151,6 +177,8 @@ private: void loadNitroEmitterInfo(const XMLNode &node, const std::string &emitter_name, int index); + void loadSpeedWeightedInfo(const XMLNode* speed_weighted_node); + void OnAnimationEnd(scene::IAnimatedMeshSceneNode *node); /** Pointer to the kart object belonging to this kart model. */ @@ -164,7 +192,7 @@ public: void loadInfo(const XMLNode &node); bool loadModels(const KartProperties &kart_properties); void update(float rotation_dt, float steer, - const float suspension[4]); + const float suspension[4], float speed); void setDefaultPhysicsPosition(const Vec3 ¢er_shift, float wheel_radius); void finishedRace(); @@ -214,6 +242,19 @@ public: const Vec3* getNitroEmittersPositon() const {return m_nitro_emitter_position;} // ------------------------------------------------------------------------ + /** Returns true if kart has nitro emitters */ + const bool hasNitroEmitters() const + {return m_has_nitro_emitter;} + // ------------------------------------------------------------------------ + /** Returns the number of speed weighted objects for this kart */ + size_t getSpeedWeightedObjectsCount() const + {return m_speed_weighted_objects.size();} + // ------------------------------------------------------------------------ + /** Returns the position of a speed weighted object relative to the kart. + * \param i Index of the object */ + const SpeedWeightedObject& getSpeedWeightedObject(int i) const + {return m_speed_weighted_objects[i];} + // ------------------------------------------------------------------------ /** Returns the length of the kart model. */ float getLength () const {return m_kart_length; } // ------------------------------------------------------------------------ diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 6f2533329..0961d0e94 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -68,7 +68,8 @@ KartProperties::KartProperties(const std::string &filename) m_nitro_max_speed_increase = m_nitro_duration = m_nitro_fade_out_time = m_suspension_stiffness = m_wheel_damping_relaxation = m_wheel_base = m_wheel_damping_compression = m_friction_slip = m_roll_influence = - m_wheel_radius = m_chassis_linear_damping = m_max_suspension_force = + m_wheel_radius = m_speed_weighted_strength_factor = m_speed_weighted_speed_factor = + m_chassis_linear_damping = m_max_suspension_force = m_chassis_angular_damping = m_suspension_rest = m_max_speed_reverse_ratio = m_rescue_vert_offset = m_upright_tolerance = m_collision_terrain_impulse = @@ -426,6 +427,12 @@ void KartProperties::getAllData(const XMLNode * root) wheels_node->get("radius", &m_wheel_radius ); } + if(const XMLNode *speed_weighted_node = root->getNode("speed-weighted")) + { + speed_weighted_node->get("strength-factor", &m_speed_weighted_strength_factor); + speed_weighted_node->get("speed-factor", &m_speed_weighted_speed_factor); + } + if(const XMLNode *friction_node = root->getNode("friction")) friction_node->get("slip", &m_friction_slip); @@ -626,6 +633,8 @@ void KartProperties::checkAllSet(const std::string &filename) CHECK_NEG(m_time_reset_steer, "turn time-reset-steer" ); CHECK_NEG(m_wheel_damping_relaxation, "wheels damping-relaxation" ); CHECK_NEG(m_wheel_damping_compression, "wheels damping-compression" ); + CHECK_NEG(m_speed_weighted_strength_factor, "speed-weighted strength-factor" ); + CHECK_NEG(m_speed_weighted_speed_factor, "speed-weighted speed-factor" ); CHECK_NEG(m_wheel_radius, "wheels radius" ); CHECK_NEG(m_friction_slip, "friction slip" ); CHECK_NEG(m_roll_influence, "stability roll-influence" ); diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index 03e82c56c..75cd08316 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -264,6 +264,10 @@ private: float m_roll_influence; float m_wheel_radius; + // Parameters for speed-weighted objects + float m_speed_weighted_strength_factor; + float m_speed_weighted_speed_factor; + /** An impulse pushing the kart down which is proportional to speed. So * the actual impulse is speed * m_downward_impulse_factor. Set it to * 0 to disable completely. Based on @@ -528,6 +532,14 @@ public: /** Returns wheel radius. */ float getWheelRadius () const {return m_wheel_radius; } + // ------------------------------------------------------------------------ + /** Returns animation strength factor for speed-weighted objects */ + float getSpeedWeightedStrengthFactor() const {return m_speed_weighted_strength_factor;} + + // ------------------------------------------------------------------------ + /** Returns animation speed factor for speed-weighted objects */ + float getSpeedWeightedSpeedFactor() const {return m_speed_weighted_speed_factor;} + // ------------------------------------------------------------------------ /** Returns the wheel base (distance front to rear axis). */ float getWheelBase () const {return m_wheel_base; } diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index c02bf7c97..b268391f8 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -53,6 +53,8 @@ void SoccerWorld::init() { WorldWithRank::init(); m_display_rank = false; + m_goal_timer = 0.f; + m_lastKartToHitBall = -1; // check for possible problems if AI karts were incorrectly added if(getNumKarts() > race_manager->getNumPlayers()) @@ -61,7 +63,6 @@ void SoccerWorld::init() exit(1); } m_goal_target = race_manager->getMaxGoal(); - printf("Max Goal: %d\n", m_goal_target); m_goal_sound = sfx_manager->createSoundSource("goal_scored"); } // init @@ -79,7 +80,11 @@ void SoccerWorld::reset() // Reset original positions for the soccer balls TrackObjectManager* tom = getTrack()->getTrackObjectManager(); assert(tom); - + m_redScorers.clear(); + m_redScoreTimes.clear(); + m_blueScorers.clear(); + m_blueScoreTimes.clear(); + m_lastKartToHitBall = -1; PtrVector& objects = tom->getObjects(); for(int i=0; ireset(); obj->getPhysics()->reset(); } - - World *world = World::getWorld(); - world->setClockMode(World::CLOCK_NONE); initKartList(); } // reset @@ -111,9 +113,22 @@ const std::string& SoccerWorld::getIdent() const */ void SoccerWorld::update(float dt) { + World *world = World::getWorld(); + WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); + if (world->getPhase() == World::GOAL_PHASE) + { + m_goal_timer += dt; + + if (m_goal_timer > 3.0f) + { + world->setPhase(WorldStatus::RACE_PHASE); + m_goal_timer = 0; + } + } + // TODO } // update @@ -131,8 +146,18 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) //printf("Score:\nTeam One %d : %d Team Two\n", m_team_goals[0], m_team_goals[1]); World *world = World::getWorld(); world->setPhase(WorldStatus::GOAL_PHASE); - world->setClockMode(World::CLOCK_COUNTDOWN, 1.0); m_goal_sound->play(); + if(m_lastKartToHitBall != -1) + { + if(first_goal){ + m_redScorers.push_back(m_lastKartToHitBall); + m_redScoreTimes.push_back(world->getTime()); + } + else{ + m_blueScorers.push_back(m_lastKartToHitBall); + m_blueScoreTimes.push_back(world->getTime()); + } + } } //m_check_goals_enabled = false; // TODO: remove? @@ -168,6 +193,13 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) // TODO: rescue the karts } // onCheckGoalTriggered +//----------------------------------------------------------------------------- +/** Sets the last kart that hit the ball, to be able to +* identify the scorer later. +*/ +void SoccerWorld::setLastKartTohitBall(unsigned int kartId){ + m_lastKartToHitBall = kartId; +} //----------------------------------------------------------------------------- /** The battle is over if only one kart is left, or no player kart. */ @@ -361,7 +393,7 @@ void SoccerWorld::initKartList() for(unsigned int i=0; igetKartModel()->getHeight()+0.5; + float arrow_pos_height = m_karts[i]->getKartModel()->getHeight()+0.5f; if(race_manager->getLocalKartInfo(i).getSoccerTeam() == SOCCER_TEAM_RED) arrowNode = irr_driver->addBillboard(core::dimension2d(0.3f,0.3f), @@ -450,8 +482,4 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, } // createKart //----------------------------------------------------------------------------- -void SoccerWorld::countdownReachedZero() -{ - World *world = World::getWorld(); - world->setPhase(World::RACE_PHASE); -} // countdownReachedZero + diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 91d512de3..ef922610a 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -49,7 +49,13 @@ private: and re-enabled when the next game can be played)*/ bool m_can_score_points; SFXBase *m_goal_sound; - + /** Timer for displaying goal text*/ + float m_goal_timer; + int m_lastKartToHitBall; + std::vector m_redScorers; + std::vector m_redScoreTimes; + std::vector m_blueScorers; + std::vector m_blueScoreTimes; public: SoccerWorld(); @@ -75,10 +81,23 @@ public: virtual void update(float dt); - virtual void countdownReachedZero(); - void onCheckGoalTriggered(bool first_goal); int getTeamLeader(unsigned int i); + void setLastKartTohitBall(unsigned int kartId); + std::vector getScorers(unsigned int team) + { + if(team == 0) + return m_redScorers; + else + return m_blueScorers; + } + std::vector getScoreTimes(unsigned int team) + { + if(team == 0) + return m_redScoreTimes; + else + return m_blueScoreTimes; + } private: void initKartList(); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index c015f665a..3174bb1e1 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -327,7 +327,7 @@ Controller* World::loadAIController(AbstractKart *kart) { Controller *controller; int turn=0; - // If different AIs should be used, adjust turn (or switch randomly + // If different AIs 8should be used, adjust turn (or switch randomly // or dependent on difficulty) switch(turn) { diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 2165e85bb..174df9da3 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -132,7 +132,7 @@ void WorldStatus::update(const float dt) case SETUP_PHASE: m_auxiliary_timer = 0.0f; m_phase = TRACK_INTRO_PHASE; - if (UserConfigParams::m_music && m_play_racestart_sounds) + if (m_play_racestart_sounds) { m_track_intro_sound->play(); } @@ -148,6 +148,9 @@ void WorldStatus::update(const float dt) if(m_track_intro_sound->getStatus()==SFXManager::SFX_PLAYING && m_auxiliary_timer<3.5f) return; + // Wait before ready phase if sounds are disabled + if(!UserConfigParams::m_sfx && m_auxiliary_timer<3.0f) + return; m_auxiliary_timer = 0.0f; if (m_play_racestart_sounds) m_prestart_sound->play(); m_phase = READY_PHASE; diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index ca0a8f6dc..0d75b9a82 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -528,6 +528,11 @@ void PhysicalObject::handleExplosion(const Vec3& pos, bool direct_hit) } // handleExplosion +// ---------------------------------------------------------------------------- +bool PhysicalObject::isSoccerBall() +{ + return m_object->isSoccerBall(); +} // ---------------------------------------------------------------------------- /* EOF */ diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index f8a699000..6f2deb4a0 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -27,6 +27,7 @@ #include "utils/vec3.hpp" #include "utils/leak_check.hpp" + class XMLNode; class TrackObject; @@ -138,6 +139,7 @@ public: virtual void handleExplosion(const Vec3& pos, bool directHit); void update (float dt); void init (); + bool isSoccerBall(); // ------------------------------------------------------------------------ /** Returns the rigid body of this physical object. */ diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 3b48e3763..0b955f97a 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -32,6 +32,7 @@ #include "physics/stk_dynamics_world.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" +#include "modes/soccer_world.hpp" // ---------------------------------------------------------------------------- /** Initialise physics. @@ -188,6 +189,12 @@ void Physics::update(float dt) const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration(), kp->getSquashSlowdown()); } + else if(obj->isSoccerBall()) + { + int kartId = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); + SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); + soccerWorld->setLastKartTohitBall(kartId); + } continue; } @@ -228,6 +235,13 @@ void Physics::update(float dt) // ------------------------------- p->getUserPointer(0)->getPointerFlyable() ->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject()); + PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject(); + if(obj->isSoccerBall()) + { + int kartId = p->getUserPointer(0)->getPointerFlyable()->getOwnerId(); + SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); + soccerWorld->setLastKartTohitBall(kartId); + } } else if(p->getUserPointer(1)->is(UserPointer::UP_KART)) diff --git a/src/states_screens/addons_screen.cpp b/src/states_screens/addons_screen.cpp index 56e18e85f..fc828cbd4 100644 --- a/src/states_screens/addons_screen.cpp +++ b/src/states_screens/addons_screen.cpp @@ -136,7 +136,7 @@ void AddonsScreen::init() m_reloading = false; - m_sort_desc = true; + m_sort_desc = false; m_sort_default = true; m_sort_col = 0; @@ -389,8 +389,8 @@ void AddonsScreen::onColumnClicked(int column_id) } else { - m_sort_default = m_sort_desc && !m_sort_default; if (!m_sort_default) m_sort_desc = !m_sort_desc; + m_sort_default = !m_sort_desc && !m_sort_default; } m_sort_col = column_id; diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp index aec1fafe5..9cfcf611a 100644 --- a/src/states_screens/feature_unlocked.cpp +++ b/src/states_screens/feature_unlocked.cpp @@ -330,7 +330,7 @@ void FeatureUnlockedCutScene::init() m_unlocked_stuff[n].m_root_gift_node = kart_model->attachModel(true); kart_model->setAnimation(KartModel::AF_DEFAULT); float susp[4]={0,0,0,0}; - kart_model->update(0.0f, 0.0f, susp); + kart_model->update(0.0f, 0.0f, susp, 0.0f); #ifdef DEBUG m_unlocked_stuff[n].m_root_gift_node->setName("unlocked kart"); diff --git a/src/states_screens/grand_prix_lose.cpp b/src/states_screens/grand_prix_lose.cpp index c5ba6ad41..d95b8c0cf 100644 --- a/src/states_screens/grand_prix_lose.cpp +++ b/src/states_screens/grand_prix_lose.cpp @@ -340,7 +340,7 @@ void GrandPrixLose::setKarts(std::vector ident_arg) kart_main_node->updateAbsolutePosition(); kart_main_node->setRotation(vector3df(0, 90, 0)); float susp[4]={0,0,0,0}; - kart_model->update(0.0f, 0.0f, susp); + kart_model->update(0.0f, 0.0f, susp, 0.0f); } else { diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index c309bf3d1..16e31c329 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -451,7 +451,7 @@ void GrandPrixWin::setKarts(const std::string idents_arg[3]) m_kart_z[n]) ); kart_main_node->setScale( core::vector3df(0.4f, 0.4f, 0.4f) ); float susp[4]={0,0,0,0}; - kart_model->update(0.0f, 0.0f, susp); + kart_model->update(0.0f, 0.0f, susp, 0.0f); } else { diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index 334fd4046..daac169ab 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -392,6 +392,11 @@ public: kart_model.getWheelGraphicsPosition(2) ); m_model_view->addModel( kart_model.getWheelModel(3), kart_model.getWheelGraphicsPosition(3) ); + for(size_t i=0 ; i < kart_model.getSpeedWeightedObjectsCount() ; i++) + { + const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); + m_model_view->addModel(obj.m_model, obj.m_position); + } m_model_view->setRotateContinuously( 35.0f ); // ---- Kart name label @@ -954,6 +959,11 @@ public: kart_model.getWheelGraphicsPosition(2) ); w3->addModel( kart_model.getWheelModel(3), kart_model.getWheelGraphicsPosition(3) ); + for(size_t i=0 ; i < kart_model.getSpeedWeightedObjectsCount() ; i++) + { + const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); + w3->addModel(obj.m_model, obj.m_position); + } w3->update(0); m_parent->m_kart_widgets[playerID].m_kart_name diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index 9b808ac23..b35a8983b 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -275,8 +275,8 @@ void RaceGUI::drawScores() default: break; } core::rect indicatorPos(offsetX, offsetY, - offsetX + m_marker_player_size/1.25, - offsetY + m_marker_player_size/1.25); + offsetX + (int)(m_marker_player_size/1.25f), + offsetY + (int)(m_marker_player_size/1.25f)); core::rect sourceRect(core::position2d(0,0), team_icon->getOriginalSize()); irr_driver->getVideoDriver()->draw2DImage(team_icon,indicatorPos,sourceRect, diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 44487a6f7..d5289786b 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -44,6 +44,7 @@ #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/string_utils.hpp" +#include DEFINE_SCREEN_SINGLETON( RaceResultGUI ); @@ -80,6 +81,12 @@ void RaceResultGUI::tearDown() { Screen::tearDown(); m_font->setMonospaceDigits(m_was_monospace); + + if (m_finish_sound != NULL && + m_finish_sound->getStatus() == SFXManager::SFX_PLAYING) + { + m_finish_sound->stop(); + } } // tearDown //----------------------------------------------------------------------------- @@ -298,14 +305,6 @@ void RaceResultGUI::onConfirm() */ void RaceResultGUI::determineTableLayout() { - if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) - { - redTeamTexture = irr_driver->getTexture( - file_manager->getTextureFile("soccer_ball_red.png")); - blueTeamTexture = irr_driver->getTexture( - file_manager->getTextureFile("soccer_ball_blue.png")); - } - GUIEngine::Widget *table_area = getWidget("result-table"); m_font = GUIEngine::getFont(); @@ -520,6 +519,8 @@ void RaceResultGUI::onUpdate(float dt, irr::video::IVideoDriver*) */ void RaceResultGUI::renderGlobal(float dt) { + bool isSoccerWorld = race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER; + m_timer += dt; assert(World::getWorld()->getPhase()==WorldStatus::RESULT_DISPLAY_PHASE); unsigned int num_karts = m_all_row_infos.size(); @@ -625,49 +626,54 @@ void RaceResultGUI::renderGlobal(float dt) // Second phase: update X and Y positions for the various animations // ================================================================= float v = 0.9f*UserConfigParams::m_width/m_time_single_scroll; - for(unsigned int i=0; im_x_pos; - float y = ri->m_y_pos; - switch(m_animation_state) + for(unsigned int i=0; i ri->m_start_at) - { // if active - ri->m_x_pos -= dt*v; - if(ri->m_x_posm_x_pos = (float)m_leftmost_column; - x = ri->m_x_pos; - } - break; - case RR_INCREASE_POINTS: - ri->m_current_displayed_points += - dt*race_manager->getPositionScore(1)/m_time_for_points; - if(ri->m_current_displayed_points>ri->m_new_overall_points) + RowInfo *ri = &(m_all_row_infos[i]); + float x = ri->m_x_pos; + float y = ri->m_y_pos; + switch(m_animation_state) { - ri->m_current_displayed_points = - (float)ri->m_new_overall_points; - } - ri->m_new_points -= - dt*race_manager->getPositionScore(1)/m_time_for_points; - if(ri->m_new_points<0) - ri->m_new_points = 0; - break; - case RR_RESORT_TABLE: - x = ri->m_x_pos - - ri->m_radius*sin(m_timer/m_time_rotation*M_PI); - y = ri->m_centre_point - + ri->m_radius*cos(m_timer/m_time_rotation*M_PI); - break; - case RR_WAIT_TILL_END: - break; - } // switch - displayOneEntry((unsigned int)x, (unsigned int)y, i, true); - } // for i + // Both states use the same scrolling: + case RR_INIT: break; // Remove compiler warning + case RR_RACE_RESULT: + case RR_OLD_GP_RESULTS: + if(m_timer > ri->m_start_at) + { // if active + ri->m_x_pos -= dt*v; + if(ri->m_x_posm_x_pos = (float)m_leftmost_column; + x = ri->m_x_pos; + } + break; + case RR_INCREASE_POINTS: + ri->m_current_displayed_points += + dt*race_manager->getPositionScore(1)/m_time_for_points; + if(ri->m_current_displayed_points>ri->m_new_overall_points) + { + ri->m_current_displayed_points = + (float)ri->m_new_overall_points; + } + ri->m_new_points -= + dt*race_manager->getPositionScore(1)/m_time_for_points; + if(ri->m_new_points<0) + ri->m_new_points = 0; + break; + case RR_RESORT_TABLE: + x = ri->m_x_pos + - ri->m_radius*sin(m_timer/m_time_rotation*M_PI); + y = ri->m_centre_point + + ri->m_radius*cos(m_timer/m_time_rotation*M_PI); + break; + case RR_WAIT_TILL_END: + break; + } // switch + displayOneEntry((unsigned int)x, (unsigned int)y, i, true); + } // for i + } + else + displaySoccerResults(); // Display highscores if (race_manager->getMajorMode() != RaceManager::MAJOR_MODE_GRAND_PRIX || @@ -744,12 +750,6 @@ void RaceResultGUI::determineGPLayout() void RaceResultGUI::displayOneEntry(unsigned int x, unsigned int y, unsigned int n, bool display_points) { - SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); - bool isSoccerMode = race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER; - int m_team_goals[2] = {soccerWorld->getScore(0), soccerWorld->getScore(1)}; - - if (isSoccerMode && n > 0) return; - RowInfo *ri = &(m_all_row_infos[n]); video::SColor color = ri->m_is_player_kart ? video::SColor(255,255,0, 0 ) @@ -770,73 +770,29 @@ void RaceResultGUI::displayOneEntry(unsigned int x, unsigned int y, // First draw the icon // ------------------- - if (!isSoccerMode) + if(ri->m_kart_icon) { - if(ri->m_kart_icon) - { - core::recti source_rect(core::vector2di(0,0), - ri->m_kart_icon->getSize()); - core::recti dest_rect(current_x, y, - current_x+m_width_icon, y+m_width_icon); - irr_driver->getVideoDriver()->draw2DImage(ri->m_kart_icon, dest_rect, - source_rect, NULL, NULL, - true); - } - - current_x += m_width_icon + m_width_column_space; + core::recti source_rect(core::vector2di(0,0), + ri->m_kart_icon->getSize()); + core::recti dest_rect(current_x, y, + current_x+m_width_icon, y+m_width_icon); + irr_driver->getVideoDriver()->draw2DImage(ri->m_kart_icon, dest_rect, + source_rect, NULL, NULL, + true); } + + current_x += m_width_icon + m_width_column_space; // Draw the name // ------------- - if (!isSoccerMode) - { - core::recti pos_name(current_x, y, - UserConfigParams::m_width, y+m_distance_between_rows); - m_font->draw(ri->m_kart_name, pos_name, color, false, false, NULL, - true /* ignoreRTL */); - current_x += m_width_kart_name + m_width_column_space; - } - // Draw icon, name of team which won in soccer mode and score - // ---------------------------------------------------------- - if (isSoccerMode) - { - core::stringw text; - irr::video::ITexture *team_icon; - if (m_team_goals[0] > m_team_goals[1]) - { - text = core::stringw(_("Red team won")); - team_icon = redTeamTexture; - } - else if (m_team_goals[0] < m_team_goals[1]) - { - text = core::stringw(_("Blue team won")); - team_icon = blueTeamTexture; - } - else - text = core::stringw(_("Draw")); + core::recti pos_name(current_x, y, + UserConfigParams::m_width, y+m_distance_between_rows); + m_font->draw(ri->m_kart_name, pos_name, color, false, false, NULL, + true /* ignoreRTL */); + current_x += m_width_kart_name + m_width_column_space; - core::recti source_rect(core::vector2di(0,0), team_icon->getSize()); - core::recti dest_rect(current_x, y, current_x+m_width_icon, y+m_width_icon); - irr_driver->getVideoDriver()->draw2DImage(team_icon, dest_rect, - source_rect, NULL, NULL, - true); - - current_x += m_width_icon + m_width_column_space; - core::recti pos_name(current_x, y, - UserConfigParams::m_width, y+m_distance_between_rows); - - m_font->draw(text, pos_name, color, false, false, NULL, true /* ignoreRTL */); - core::dimension2du rect = m_font->getDimension(text.c_str()); - current_x += rect.Width + 2*m_width_column_space; - - text = core::stringw(m_team_goals[0]) + " : " + core::stringw(m_team_goals[1]); - dest_rect = core::recti(current_x, y, current_x+100, y+10); - m_font->draw(text, dest_rect, color, false, false, NULL, true /* ignoreRTL */); - rect = m_font->getDimension(text.c_str()); - current_x += rect.Width + 2*m_width_column_space; - } - + // Draw the time except in FTL mode // -------------------------------- if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_FOLLOW_LEADER) @@ -882,6 +838,145 @@ void RaceResultGUI::displayOneEntry(unsigned int x, unsigned int y, } } // displayOneEntry +//----------------------------------------------------------------------------- +void RaceResultGUI::displaySoccerResults() +{ + + //Draw win text + core::stringw resultText; + static video::SColor color = video::SColor(255, 255, 255, 255); + gui::IGUIFont* font = GUIEngine::getTitleFont(); + int currX = UserConfigParams::m_width/2; + RowInfo *ri = &(m_all_row_infos[0]); + int currY = (int)ri->m_y_pos; + SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); + int teamScore[2] = {soccerWorld->getScore(0), soccerWorld->getScore(1)}; + + GUIEngine::Widget *table_area = getWidget("result-table"); + int height = table_area->m_h + table_area->m_y; + + if(teamScore[0] > teamScore[1]) + { + resultText = _("Red Team Wins"); + } + else if(teamScore[1] > teamScore[0]) + { + resultText = _("Blue Team Wins"); + } + else + { + //Cannot really happen now. Only in time limited matches. + resultText = _("It's a draw"); + } + core::rect pos(currX, currY, currX, currY); + font->draw(resultText.c_str(), pos, color, true, true); + + core::dimension2du rect = m_font->getDimension(resultText.c_str()); + + //Draw team scores: + currY += rect.Height; + currX /= 2; + irr::video::ITexture* redTeamIcon = irr_driver->getTexture( + file_manager->getTextureFile("soccer_ball_red.png")); + irr::video::ITexture* blueTeamIcon = irr_driver->getTexture( + file_manager->getTextureFile("soccer_ball_blue.png")); + + core::recti sourceRect(core::vector2di(0,0), redTeamIcon->getSize()); + core::recti destRect(currX, currY, currX+redTeamIcon->getSize().Width/2, + currY+redTeamIcon->getSize().Height/2); + irr_driver->getVideoDriver()->draw2DImage(redTeamIcon, destRect,sourceRect, + NULL,NULL, true); + currX += UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; + destRect = core::recti(currX, currY, currX+redTeamIcon->getSize().Width/2, + currY+redTeamIcon->getSize().Height/2); + irr_driver->getVideoDriver()->draw2DImage(blueTeamIcon,destRect,sourceRect, + NULL, NULL, true); + + resultText = StringUtils::toWString(teamScore[1]); + rect = m_font->getDimension(resultText.c_str()); + currX += redTeamIcon->getSize().Width/4; + currY += redTeamIcon->getSize().Height/2 + rect.Height/4; + pos = core::rect(currX, currY, currX, currY); + color = video::SColor(255,255,255,255); + font->draw(resultText.c_str(), pos, color, true, false); + + currX -= UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; + resultText = StringUtils::toWString(teamScore[0]); + pos = core::rect(currX,currY,currX,currY); + font->draw(resultText.c_str(), pos, color, true, false); + + int centerX = UserConfigParams::m_width/2; + pos = core::rect(centerX, currY, centerX, currY); + font->draw("-", pos, color, true, false); + + //Draw goal scorers: + //The red scorers: + currY += rect.Height/2 + rect.Height/4; + font = GUIEngine::getSmallFont(); + std::vector scorers = soccerWorld->getScorers(0); + std::vector scoreTimes = soccerWorld->getScoreTimes(0); + irr::video::ITexture* scorerIcon; + + int prevY = currY; + for(unsigned int i=0; igetKart(scorers.at(i))-> + getKartProperties()->getName(); + resultText.append(" "); + resultText.append(StringUtils::timeToString(scoreTimes.at(i)).c_str()); + rect = m_font->getDimension(resultText.c_str()); + + if(height-prevY < ((short)scorers.size()+1)*(short)rect.Height) + currY += (height-prevY)/((short)scorers.size()+1); + else + currY += rect.Height; + + if(currY > height) break; + + pos = core::rect(currX,currY,currX,currY); + font->draw(resultText,pos, color, true, false); + scorerIcon = soccerWorld->getKart(scorers.at(i))-> + getKartProperties()->getIconMaterial()->getTexture(); + sourceRect = core::recti(core::vector2di(0,0), scorerIcon->getSize()); + irr::u32 offsetX = GUIEngine::getFont()->getDimension(resultText.c_str()).Width/2; + destRect = core::recti(currX-offsetX-30, currY, currX-offsetX, currY+ 30); + irr_driver->getVideoDriver()->draw2DImage(scorerIcon, destRect, sourceRect, + NULL, NULL, true); + } + + //The blue scorers: + currY = prevY; + currX += UserConfigParams::m_width/2 - redTeamIcon->getSize().Width/2; + scorers = soccerWorld->getScorers(1); + scoreTimes = soccerWorld->getScoreTimes(1); + for(unsigned int i=0; igetKart(scorers.at(i))-> + getKartProperties()->getName(); + resultText.append(" "); + resultText.append(StringUtils::timeToString(scoreTimes.at(i)).c_str()); + rect = m_font->getDimension(resultText.c_str()); + + if(height-prevY < ((short)scorers.size()+1)*(short)rect.Height) + currY += (height-prevY)/((short)scorers.size()+1); + else + currY += rect.Height; + + if(currY > height) break; + + pos = core::rect(currX,currY,currX,currY); + font->draw(resultText,pos, color, true, false); + scorerIcon = soccerWorld->getKart(scorers.at(i))-> + getKartProperties()->getIconMaterial()->getTexture(); + sourceRect = core::recti(core::vector2di(0,0), scorerIcon->getSize()); + irr::u32 offsetX = GUIEngine::getFont()->getDimension(resultText.c_str()).Width/2; + + destRect = core::recti(currX-offsetX-30, currY, currX-offsetX, currY+ 30); + irr_driver->getVideoDriver()->draw2DImage(scorerIcon, destRect, sourceRect, + NULL, NULL, true); + } +} + //----------------------------------------------------------------------------- void RaceResultGUI::clearHighscores() diff --git a/src/states_screens/race_result_gui.hpp b/src/states_screens/race_result_gui.hpp index cc0cc7476..902a7380c 100644 --- a/src/states_screens/race_result_gui.hpp +++ b/src/states_screens/race_result_gui.hpp @@ -110,8 +110,6 @@ private: }; // Rowinfo /** The team icons. */ - video::ITexture *redTeamTexture; - video::ITexture *blueTeamTexture; std::vector m_all_row_infos; @@ -198,6 +196,7 @@ private: void displayGPProgress(); void cleanupGPProgress(); void displayHighScores(); + void displaySoccerResults(); public: RaceResultGUI(); diff --git a/src/states_screens/soccer_setup_screen.cpp b/src/states_screens/soccer_setup_screen.cpp index b492301c4..2e3ae4cee 100644 --- a/src/states_screens/soccer_setup_screen.cpp +++ b/src/states_screens/soccer_setup_screen.cpp @@ -35,7 +35,7 @@ using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( SoccerSetupScreen ); #define KART_CONTINUOUS_ROTATION_SPEED 35.f -#define KART_CONFIRMATION_ROTATION_SPEED 0.f +#define KART_CONFIRMATION_ROTATION_SPEED 4.f #define KART_CONFIRMATION_TARGET_ANGLE 10.f // ----------------------------------------------------------------------------- @@ -56,7 +56,7 @@ void SoccerSetupScreen::eventCallback(Widget* widget, const std::string& name, c if(name == "continue") { StateManager::get()->pushScreen( ArenasScreen::getInstance() ); - race_manager->setMaxGoal(getWidget("goalamount")->getValue()); + race_manager->setMaxGoal(getWidget("goalamount")->getValue()); input_manager->setMasterPlayerOnly(true); } else if (name == "back") @@ -184,12 +184,28 @@ GUIEngine::EventPropagation SoccerSetupScreen::filterActions( PlayerAction acti switch(action) { case PA_MENU_LEFT: - if (bt_continue->isFocusedForPlayer(PLAYER_ID_GAME_MASTER)) + if (bt_continue->isFocusedForPlayer(PLAYER_ID_GAME_MASTER) && + m_kart_view_info[playerId].confirmed == false) + { team_switch = SOCCER_TEAM_RED; + + for(int i=0 ; i < nb_players ; i++) + { + m_kart_view_info[i].view->unsetBadge(BAD_BADGE); + } + } break; case PA_MENU_RIGHT: - if (bt_continue->isFocusedForPlayer(PLAYER_ID_GAME_MASTER)) + if (bt_continue->isFocusedForPlayer(PLAYER_ID_GAME_MASTER) && + m_kart_view_info[playerId].confirmed == false) + { team_switch = SOCCER_TEAM_BLUE; + + for(int i=0 ; i < nb_players ; i++) + { + m_kart_view_info[i].view->unsetBadge(BAD_BADGE); + } + } break; case PA_MENU_UP: if (playerId != PLAYER_ID_GAME_MASTER) @@ -204,13 +220,28 @@ GUIEngine::EventPropagation SoccerSetupScreen::filterActions( PlayerAction acti if (!bt_continue->isFocusedForPlayer(PLAYER_ID_GAME_MASTER) || areAllKartsConfirmed()) return result; + if (getNumConfirmedKarts() > nb_players-2 && + (getNumKartsInTeam(SOCCER_TEAM_RED) == 0 || + getNumKartsInTeam(SOCCER_TEAM_BLUE) == 0)) + { + if (!m_kart_view_info[playerId].confirmed) + { + sfx_manager->quickSound( "anvil" ); + m_kart_view_info[playerId].view->setBadge(BAD_BADGE); + } + return EVENT_BLOCK; + } + // Confirm team selection for(int i=0 ; i < nb_players ; i++) { - if(m_kart_view_info[i].local_player_id == playerId) + if(m_kart_view_info[i].local_player_id == playerId && + m_kart_view_info[i].confirmed == false) { m_kart_view_info[i].confirmed = true; m_kart_view_info[i].view->setRotateTo( KART_CONFIRMATION_TARGET_ANGLE, KART_CONFIRMATION_ROTATION_SPEED ); + m_kart_view_info[i].view->setBadge(OK_BADGE); + sfx_manager->quickSound( "wee" ); break; } } @@ -230,8 +261,9 @@ GUIEngine::EventPropagation SoccerSetupScreen::filterActions( PlayerAction acti { m_kart_view_info[i].confirmed = false; m_kart_view_info[i].view->setRotateContinuously( KART_CONTINUOUS_ROTATION_SPEED ); - break; + m_kart_view_info[i].view->unsetBadge(OK_BADGE); } + m_kart_view_info[i].view->unsetBadge(BAD_BADGE); } result = EVENT_BLOCK; break; @@ -239,7 +271,7 @@ GUIEngine::EventPropagation SoccerSetupScreen::filterActions( PlayerAction acti default: break; } - + if(team_switch != SOCCER_TEAM_NONE) // A player wants to change his team? { // Find the corresponding kart view, update its team and update the layout @@ -288,6 +320,30 @@ bool SoccerSetupScreen::areAllKartsConfirmed() const return all_confirmed; } +int SoccerSetupScreen::getNumKartsInTeam(int team) +{ + int karts_in_team = 0; + int nb_players = m_kart_view_info.size(); + for(int i=0 ; i < nb_players ; i++) + { + if(m_kart_view_info[i].team == team) + karts_in_team++; + } + return karts_in_team; +} + +int SoccerSetupScreen::getNumConfirmedKarts() +{ + int confirmed_karts = 0; + int nb_players = m_kart_view_info.size(); + for(int i=0 ; i < nb_players ; i++) + { + if(m_kart_view_info[i].confirmed == true) + confirmed_karts++; + } + return confirmed_karts; +} + void SoccerSetupScreen::updateKartViewsLayout() { Widget* central_div = getWidget("central_div"); diff --git a/src/states_screens/soccer_setup_screen.hpp b/src/states_screens/soccer_setup_screen.hpp index 0934cd8bf..aa34e0f62 100644 --- a/src/states_screens/soccer_setup_screen.hpp +++ b/src/states_screens/soccer_setup_screen.hpp @@ -72,6 +72,8 @@ public: private: bool areAllKartsConfirmed() const; + int getNumKartsInTeam(int team); + int getNumConfirmedKarts(); void updateKartViewsLayout(); }; From a1ee11596379c2f4ad081be61cdd0b5189860456 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 23 Oct 2013 01:14:08 +0000 Subject: [PATCH 014/170] Split AIBaseController to AIBaseController and AIBaseLapController. Moved the functions independent of QuadGraph to AIBaseController. Added AIBaseController as friend of AIProperties. New class for BattleAI which inherits from AIBaseController. Its messy, need hiker. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14294 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- CMakeLists.txt | 2 + src/karts/controller/ai_base_controller.cpp | 202 +++++++++++++++++- src/karts/controller/ai_base_controller.hpp | 32 +++ .../controller/ai_base_lap_controller.cpp | 195 +---------------- .../controller/ai_base_lap_controller.hpp | 31 +-- src/karts/controller/ai_properties.cpp | 3 + src/karts/controller/ai_properties.hpp | 6 +- src/karts/controller/battle_ai.cpp | 49 +++++ src/karts/controller/battle_ai.hpp | 75 +++++++ src/karts/controller/skidding_ai.cpp | 72 +++++-- src/karts/explosion_animation.cpp | 5 + 11 files changed, 430 insertions(+), 242 deletions(-) create mode 100644 src/karts/controller/battle_ai.cpp create mode 100644 src/karts/controller/battle_ai.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ac5dcfde9..823832976 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,8 @@ endif() if(APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch i386") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch i386 -F/Library/Frameworks") +elseif(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") # Enable multi-processor compilation (faster) endif() # OpenAL diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index bda0ad3d2..18cc145b0 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -23,6 +23,7 @@ #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" +#include "karts/skidding_properties.hpp" #include "karts/controller/ai_properties.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -32,4 +33,203 @@ AIBaseController::AIBaseController(AbstractKart *kart, StateManager::ActivePlayer *player) : Controller(kart, player) { -} \ No newline at end of file + m_kart = kart; + m_kart_length = m_kart->getKartLength(); + m_kart_width = m_kart->getKartWidth(); + m_ai_properties = + m_kart->getKartProperties()->getAIPropertiesForDifficulty(); + +} + +//----------------------------------------------------------------------------- +/** In debug mode when the user specified --ai-debug on the command line set + * the name of the controller as on-screen text, so that the different AI + * controllers can be distinguished. + * \param name Name of the controller. +*/ +void AIBaseController::setControllerName(const std::string &name) +{ +#ifdef DEBUG + if(m_ai_debug && !UserConfigParams::m_camera_debug) + m_kart->setOnScreenText(core::stringw(name.c_str()).c_str()); +#endif + Controller::setControllerName(name); +} // setControllerName + +//----------------------------------------------------------------------------- +/** Computes the steering angle to reach a certain point. The function will + * request steering by setting the steering angle to maximum steer angle + * times skidding factor. + * \param point Point to steer towards. + * \param skidding_factor Increase factor for steering when skidding. + * \return Steer angle to use to reach this point. + */ +float AIBaseController::steerToPoint(const Vec3 &point) +{ + + // First translate and rotate the point the AI is aiming + // at into the kart's local coordinate system. + btQuaternion q(btVector3(0,1,0), -m_kart->getHeading()); + Vec3 p = point - m_kart->getXYZ(); + Vec3 lc = quatRotate(q, p); + + // The point the kart is aiming at can be reached 'incorrectly' if the + // point is below the y=x line: Instead of aiming at that point directly + // the point will be reached on its way 'back' after a more than 90 + // degree turn in the circle, i.e.: + // | So the point p (belolw the y=x line) can not be + // | ---\ reached on any circle directly, so it is reached + // | / \ on the indicated way. Since this is not the way + // |/ p we expect a kart to drive (it will result in the + // +-------------- kart doing slaloms, not driving straight), the + // kart will trigger skidding to allow for sharper turns, and hopefully + // the situation will change so that the point p can then be reached + // with a normal turn (it usually works out this way quite easily). + if(fabsf(lc.getX()) > fabsf(lc.getZ())) + { + // Explicitely set the steering angle high enough to that the + // steer function will request skidding. 0.1 is added in case + // of floating point errors. + if(lc.getX()>0) + return m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold+0.1f; + else + return -m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold-0.1f; + } + + // Now compute the nexessary radius for the turn. After getting the + // kart local coordinates for the point to aim at, the kart is at + // (0,0) facing straight ahead. The center of the rotation is then + // on the X axis and can be computed by the fact that the distance + // to the kart and to the point to aim at must be the same: + // r*r = (r-x)*(r-x) + y*y + // where r is the radius (= position on the X axis), and x, y are the + // local coordinates of the point to aim at. Solving for r + // results in r = (x*x+y*y)/2x + float radius = (lc.getX()*lc.getX() + lc.getZ()*lc.getZ()) + / (2.0f*lc.getX()); + + // sin(steern_angle) = wheel_base / radius: + float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius; + + // If the wheel base is too long (i.e. the minimum radius is too large + // to actually reach the target), make sure that skidding is used + if(sin_steer_angle <= -1.0f) + return -m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold-0.1f; + if(sin_steer_angle >= 1.0f) + return m_kart->getMaxSteerAngle() + *m_ai_properties->m_skidding_threshold+0.1f; + float steer_angle = asin(sin_steer_angle); + + // After doing the exact computation, we now return an 'oversteered' + // value. This actually helps in making tighter turns, and also in + // very tight turns on narrow roads (where following the circle might + // actually take the kart off track) it forces smaller turns. + // It does not actually hurt to steer too much, since the steering + // will be adjusted every frame. + return steer_angle*2.0f; +} // steerToPoint + +//----------------------------------------------------------------------------- +/** Normalises an angle to be between -pi and _ pi. + * \param angle Angle to normalise. + * \return Normalised angle. + */ +float AIBaseController::normalizeAngle(float angle) +{ + // Add an assert here since we had cases in which an invalid angle + // was given, resulting in an endless loop (floating point precision, + // e.g.: 1E17 - 2*M_PI = 1E17 + assert(angle >= -4*M_PI && angle <= 4*M_PI); + while( angle > 2*M_PI ) angle -= 2*M_PI; + while( angle < -2*M_PI ) angle += 2*M_PI; + + if( angle > M_PI ) angle -= 2*M_PI; + else if( angle < -M_PI ) angle += 2*M_PI; + + return angle; +} // normalizeAngle + +//----------------------------------------------------------------------------- +/** Converts the steering angle to a lr steering in the range of -1 to 1. + * If the steering angle is too great, it will also trigger skidding. This + * function uses a 'time till full steer' value specifying the time it takes + * for the wheel to reach full left/right steering similar to player karts + * when using a digital input device. The parameter is defined in the kart + * properties and helps somewhat to make AI karts more 'pushable' (since + * otherwise the karts counter-steer to fast). + * It also takes the effect of a plunger into account by restricting the + * actual steer angle to 50% of the maximum. + * \param angle Steering angle. + * \param dt Time step. + */ +void AIBaseController::setSteering(float angle, float dt) +{ + float steer_fraction = angle / m_kart->getMaxSteerAngle(); + if(!doSkid(steer_fraction)) + m_controls->m_skid = KartControl::SC_NONE; + else + m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT + : KartControl::SC_LEFT; + float old_steer = m_controls->m_steer; + + if (steer_fraction > 1.0f) steer_fraction = 1.0f; + else if(steer_fraction < -1.0f) steer_fraction = -1.0f; + + if(m_kart->getBlockedByPlungerTime()>0) + { + if (steer_fraction > 0.5f) steer_fraction = 0.5f; + else if(steer_fraction < -0.5f) steer_fraction = -0.5f; + } + + // The AI has its own 'time full steer' value (which is the time + float max_steer_change = dt/m_ai_properties->m_time_full_steer; + if(old_steer < steer_fraction) + { + m_controls->m_steer = (old_steer+max_steer_change > steer_fraction) + ? steer_fraction : old_steer+max_steer_change; + } + else + { + m_controls->m_steer = (old_steer-max_steer_change < steer_fraction) + ? steer_fraction : old_steer-max_steer_change; + } +} // setSteering + +// ---------------------------------------------------------------------------- +/** Determines if the kart should skid. The base implementation enables + * skidding if a sharp turn is needed (which is for the old skidding + * implementation). + * \param steer_fraction The steering fraction as computed by the + * AIBaseController. + * \return True if the kart should skid. + */ + +// ------------------------------------------------------------------------ +/** Certain AI levels will not receive a slipstream bonus in order to + * be not as hard. + */ +bool AIBaseController::disableSlipstreamBonus() const +{ + return m_ai_properties->disableSlipstreamUsage(); +} // disableSlipstreamBonus + + +bool AIBaseController::doSkid(float steer_fraction) +{ + // Disable skidding when a plunger is in the face + if(m_kart->getBlockedByPlungerTime()>0) return false; + + // FIXME: Disable skidding for now if the new skidding + // code is activated, since the AI can not handle this + // properly. + if(m_kart->getKartProperties()->getSkiddingProperties() + ->getSkidVisualTime()>0) + return false; + + // Otherwise return if we need a sharp turn (which is + // for the old skidding implementation). + return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold; +} // doSkid \ No newline at end of file diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index d8a89420c..0febdadbb 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -23,6 +23,7 @@ class AIProperties; class LinearWorld; +class ThreeStrikesBattle; class QuadGraph; class BattleGraph; class Track; @@ -34,6 +35,35 @@ class AIBaseController : public Controller +protected: + /** Length of the kart, storing it here saves many function calls. */ + float m_kart_length; + + /** Cache width of kart. */ + float m_kart_width; + + + /** Keep a pointer to the track to reduce calls */ + Track *m_track; + + + + /** A pointer to the AI properties for this kart. */ + const AIProperties *m_ai_properties; + + + + /** The current node the kart is on. This can be different from the value + * in LinearWorld, since it takes the chosen path of the AI into account + * (e.g. the closest point in LinearWorld might be on a branch not + * chosen by the AI). */ + int m_track_node; + + virtual void setSteering (float angle, float dt); + void setControllerName(const std::string &name); + float steerToPoint(const Vec3 &point); + float normalizeAngle(float angle); + virtual bool doSkid(float steer_fraction); public: @@ -41,5 +71,7 @@ public: StateManager::ActivePlayer *player=NULL); virtual ~AIBaseController() {}; + virtual bool disableSlipstreamBonus() const; + }; #endif \ No newline at end of file diff --git a/src/karts/controller/ai_base_lap_controller.cpp b/src/karts/controller/ai_base_lap_controller.cpp index d873cc945..14aa9636d 100644 --- a/src/karts/controller/ai_base_lap_controller.cpp +++ b/src/karts/controller/ai_base_lap_controller.cpp @@ -88,12 +88,7 @@ AIBaseLapController::AIBaseLapController(AbstractKart *kart, StateManager::ActivePlayer *player) : AIBaseController(kart, player) { - m_kart = kart; - m_kart_length = m_kart->getKartLength(); - m_kart_width = m_kart->getKartWidth(); - m_ai_properties = - m_kart->getKartProperties()->getAIPropertiesForDifficulty(); - + if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES && race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER) { @@ -123,20 +118,7 @@ void AIBaseLapController::reset() m_collision_times.clear(); } // reset -//----------------------------------------------------------------------------- -/** In debug mode when the user specified --ai-debug on the command line set - * the name of the controller as on-screen text, so that the different AI - * controllers can be distinguished. - * \param name Name of the controller. -*/ -void AIBaseLapController::setControllerName(const std::string &name) -{ -#ifdef DEBUG - if(m_ai_debug && !UserConfigParams::m_camera_debug) - m_kart->setOnScreenText(core::stringw(name.c_str()).c_str()); -#endif - Controller::setControllerName(name); -} // setControllerName + //----------------------------------------------------------------------------- /** Triggers a recomputation of the path to use, so that the AI does not @@ -342,177 +324,4 @@ float AIBaseLapController::steerToAngle(const unsigned int sector, return steer_angle; } // steerToAngle -//----------------------------------------------------------------------------- -/** Computes the steering angle to reach a certain point. The function will - * request steering by setting the steering angle to maximum steer angle - * times skidding factor. - * \param point Point to steer towards. - * \param skidding_factor Increase factor for steering when skidding. - * \return Steer angle to use to reach this point. - */ -float AIBaseLapController::steerToPoint(const Vec3 &point) -{ - // First translate and rotate the point the AI is aiming - // at into the kart's local coordinate system. - btQuaternion q(btVector3(0,1,0), -m_kart->getHeading()); - Vec3 p = point - m_kart->getXYZ(); - Vec3 lc = quatRotate(q, p); - - // The point the kart is aiming at can be reached 'incorrectly' if the - // point is below the y=x line: Instead of aiming at that point directly - // the point will be reached on its way 'back' after a more than 90 - // degree turn in the circle, i.e.: - // | So the point p (belolw the y=x line) can not be - // | ---\ reached on any circle directly, so it is reached - // | / \ on the indicated way. Since this is not the way - // |/ p we expect a kart to drive (it will result in the - // +-------------- kart doing slaloms, not driving straight), the - // kart will trigger skidding to allow for sharper turns, and hopefully - // the situation will change so that the point p can then be reached - // with a normal turn (it usually works out this way quite easily). - if(fabsf(lc.getX()) > fabsf(lc.getZ())) - { - // Explicitely set the steering angle high enough to that the - // steer function will request skidding. 0.1 is added in case - // of floating point errors. - if(lc.getX()>0) - return m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold+0.1f; - else - return -m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold-0.1f; - } - - // Now compute the nexessary radius for the turn. After getting the - // kart local coordinates for the point to aim at, the kart is at - // (0,0) facing straight ahead. The center of the rotation is then - // on the X axis and can be computed by the fact that the distance - // to the kart and to the point to aim at must be the same: - // r*r = (r-x)*(r-x) + y*y - // where r is the radius (= position on the X axis), and x, y are the - // local coordinates of the point to aim at. Solving for r - // results in r = (x*x+y*y)/2x - float radius = (lc.getX()*lc.getX() + lc.getZ()*lc.getZ()) - / (2.0f*lc.getX()); - - // sin(steern_angle) = wheel_base / radius: - float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius; - - // If the wheel base is too long (i.e. the minimum radius is too large - // to actually reach the target), make sure that skidding is used - if(sin_steer_angle <= -1.0f) - return -m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold-0.1f; - if(sin_steer_angle >= 1.0f) - return m_kart->getMaxSteerAngle() - *m_ai_properties->m_skidding_threshold+0.1f; - float steer_angle = asin(sin_steer_angle); - - // After doing the exact computation, we now return an 'oversteered' - // value. This actually helps in making tighter turns, and also in - // very tight turns on narrow roads (where following the circle might - // actually take the kart off track) it forces smaller turns. - // It does not actually hurt to steer too much, since the steering - // will be adjusted every frame. - return steer_angle*2.0f; -} // steerToPoint - -//----------------------------------------------------------------------------- -/** Normalises an angle to be between -pi and _ pi. - * \param angle Angle to normalise. - * \return Normalised angle. - */ -float AIBaseLapController::normalizeAngle(float angle) -{ - // Add an assert here since we had cases in which an invalid angle - // was given, resulting in an endless loop (floating point precision, - // e.g.: 1E17 - 2*M_PI = 1E17 - assert(angle >= -4*M_PI && angle <= 4*M_PI); - while( angle > 2*M_PI ) angle -= 2*M_PI; - while( angle < -2*M_PI ) angle += 2*M_PI; - - if( angle > M_PI ) angle -= 2*M_PI; - else if( angle < -M_PI ) angle += 2*M_PI; - - return angle; -} // normalizeAngle - -//----------------------------------------------------------------------------- -/** Converts the steering angle to a lr steering in the range of -1 to 1. - * If the steering angle is too great, it will also trigger skidding. This - * function uses a 'time till full steer' value specifying the time it takes - * for the wheel to reach full left/right steering similar to player karts - * when using a digital input device. The parameter is defined in the kart - * properties and helps somewhat to make AI karts more 'pushable' (since - * otherwise the karts counter-steer to fast). - * It also takes the effect of a plunger into account by restricting the - * actual steer angle to 50% of the maximum. - * \param angle Steering angle. - * \param dt Time step. - */ -void AIBaseLapController::setSteering(float angle, float dt) -{ - float steer_fraction = angle / m_kart->getMaxSteerAngle(); - if(!doSkid(steer_fraction)) - m_controls->m_skid = KartControl::SC_NONE; - else - m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT - : KartControl::SC_LEFT; - float old_steer = m_controls->m_steer; - - if (steer_fraction > 1.0f) steer_fraction = 1.0f; - else if(steer_fraction < -1.0f) steer_fraction = -1.0f; - - if(m_kart->getBlockedByPlungerTime()>0) - { - if (steer_fraction > 0.5f) steer_fraction = 0.5f; - else if(steer_fraction < -0.5f) steer_fraction = -0.5f; - } - - // The AI has its own 'time full steer' value (which is the time - float max_steer_change = dt/m_ai_properties->m_time_full_steer; - if(old_steer < steer_fraction) - { - m_controls->m_steer = (old_steer+max_steer_change > steer_fraction) - ? steer_fraction : old_steer+max_steer_change; - } - else - { - m_controls->m_steer = (old_steer-max_steer_change < steer_fraction) - ? steer_fraction : old_steer-max_steer_change; - } -} // setSteering - -// ---------------------------------------------------------------------------- -/** Determines if the kart should skid. The base implementation enables - * skidding if a sharp turn is needed (which is for the old skidding - * implementation). - * \param steer_fraction The steering fraction as computed by the - * AIBaseLapController. - * \return True if the kart should skid. - */ -bool AIBaseLapController::doSkid(float steer_fraction) -{ - // Disable skidding when a plunger is in the face - if(m_kart->getBlockedByPlungerTime()>0) return false; - - // FIXME: Disable skidding for now if the new skidding - // code is activated, since the AI can not handle this - // properly. - if(m_kart->getKartProperties()->getSkiddingProperties() - ->getSkidVisualTime()>0) - return false; - - // Otherwise return if we need a sharp turn (which is - // for the old skidding implementation). - return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold; -} // doSkid -// ------------------------------------------------------------------------ -/** Certain AI levels will not receive a slipstream bonus in order to - * be not as hard. - */ -bool AIBaseLapController::disableSlipstreamBonus() const -{ - return m_ai_properties->disableSlipstreamUsage(); -} // disableSlipstreamBonus diff --git a/src/karts/controller/ai_base_lap_controller.hpp b/src/karts/controller/ai_base_lap_controller.hpp index 3fdd0ee7b..637a7e7d4 100644 --- a/src/karts/controller/ai_base_lap_controller.hpp +++ b/src/karts/controller/ai_base_lap_controller.hpp @@ -44,27 +44,10 @@ private: bool m_stuck_trigger_rescue; protected: - /** Length of the kart, storing it here saves many function calls. */ - float m_kart_length; - - /** Cache width of kart. */ - float m_kart_width; - - /** Keep a pointer to the track to reduce calls */ - Track *m_track; - + /** Keep a pointer to world. */ LinearWorld *m_world; - /** A pointer to the AI properties for this kart. */ - const AIProperties *m_ai_properties; - - /** The current node the kart is on. This can be different from the value - * in LinearWorld, since it takes the chosen path of the AI into account - * (e.g. the closest point in LinearWorld might be on a branch not - * chosen by the AI). */ - int m_track_node; - /** Which of the successors of a node was selected by the AI. */ std::vector m_successor_index; /** For each node in the graph this list contains the chosen next node. @@ -80,13 +63,13 @@ protected: virtual void update (float delta) ; virtual unsigned int getNextSector(unsigned int index); virtual void newLap (int lap); - virtual void setControllerName(const std::string &name); - virtual void setSteering (float angle, float dt); + //virtual void setControllerName(const std::string &name); + float steerToAngle (const unsigned int sector, const float angle); - float steerToPoint (const Vec3 &point); - float normalizeAngle(float angle); + + void computePath(); - virtual bool doSkid(float steer_fraction); + // ------------------------------------------------------------------------ /** Nothing special to do when the race is finished. */ virtual void raceFinished() {}; @@ -113,7 +96,7 @@ public: virtual bool isPlayerController() const { return false; } virtual void action(PlayerAction action, int value) {}; virtual void skidBonusTriggered() {}; - virtual bool disableSlipstreamBonus() const; + }; // AIBaseLapController #endif diff --git a/src/karts/controller/ai_properties.cpp b/src/karts/controller/ai_properties.cpp index 49ce34ee7..f5bc0b99f 100644 --- a/src/karts/controller/ai_properties.cpp +++ b/src/karts/controller/ai_properties.cpp @@ -38,6 +38,7 @@ AIProperties::AIProperties(RaceManager::Difficulty difficulty) m_skidding_threshold = UNDEFINED; m_min_start_delay = UNDEFINED; m_max_start_delay = UNDEFINED; + m_shield_incoming_radius = UNDEFINED; m_false_start_probability = UNDEFINED; m_make_use_of_slipstream = false; m_collect_avoid_items = false; @@ -68,6 +69,7 @@ void AIProperties::load(const XMLNode *ai_node) ai_node->get("collect-avoid-items", &m_collect_avoid_items ); ai_node->get("handle-bomb", &m_handle_bomb ); ai_node->get("skidding-threshold", &m_skidding_threshold ); + ai_node->get("shield-incoming-radius", &m_shield_incoming_radius ); ai_node->get("false-start-probability", &m_false_start_probability ); ai_node->get("min-start-delay", &m_min_start_delay ); ai_node->get("max-start-delay", &m_max_start_delay ); @@ -109,6 +111,7 @@ void AIProperties::checkAllSet(const std::string &filename) const CHECK_NEG(m_bad_item_closeness_2, "bad-item-closeness" ); CHECK_NEG(m_straight_length_for_zipper,"straight-length-for-zipper"); CHECK_NEG(m_skidding_threshold, "skidding-threshold" ); + CHECK_NEG(m_shield_incoming_radius, "shield-incoming-radius" ); CHECK_NEG(m_false_start_probability, "false-start-probability" ); CHECK_NEG(m_min_start_delay, "min-start-delay" ); CHECK_NEG(m_max_start_delay, "max-start-delay" ); diff --git a/src/karts/controller/ai_properties.hpp b/src/karts/controller/ai_properties.hpp index 8f62edf30..45a474f96 100644 --- a/src/karts/controller/ai_properties.hpp +++ b/src/karts/controller/ai_properties.hpp @@ -42,6 +42,7 @@ public: //LEAK_CHECK(); protected: // Give them access to the members + friend class AIBaseController; friend class AIBaseLapController; friend class SkiddingAI; @@ -79,7 +80,10 @@ protected: /** To determine the probability of selecting an item. */ InterpolationArray m_collect_item_probability; - + + /** Distance at which a detected projectile triggers a shield. */ + float m_shield_incoming_radius; + /** Probability of a false start. Note that Nolok in boss battle will never * have a false start. */ float m_false_start_probability; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp new file mode 100644 index 000000000..e14db9f77 --- /dev/null +++ b/src/karts/controller/battle_ai.cpp @@ -0,0 +1,49 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2004-2005 Steve Baker +// Copyright (C) 2006-2007 Eduardo Hernandez Munoz +// Copyright (C) 2008-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 "karts/controller/battle_ai.hpp" + +#include "modes/three_strikes_battle.hpp" + + +BattleAI::BattleAI(AbstractKart *kart, + StateManager::ActivePlayer *player) + : AIBaseController(kart, player) +{ + + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES) + { + m_world = dynamic_cast(World::getWorld()); + m_track = m_world->getTrack(); + + } + else + { + // Those variables are not defined in a battle mode (m_world is + // a linear world, since it assumes the existance of drivelines) + m_world = NULL; + m_track = NULL; + } + // Don't call our own setControllerName, since this will add a + // billboard showing 'AIBaseLapController' to the kar. + Controller::setControllerName("BattleAI"); + +} \ No newline at end of file diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp new file mode 100644 index 000000000..634b044e6 --- /dev/null +++ b/src/karts/controller/battle_ai.hpp @@ -0,0 +1,75 @@ + +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2004-2005 Steve Baker +// Copyright (C) 2006-2007 Eduardo Hernandez Munoz +// Copyright (C) 2010 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 HEADER_BATTLE_AI_HPP +#define HEADER_BATTLE_AI__HPP + +#include "karts/controller/ai_base_controller.hpp" +#include "race/race_manager.hpp" +#include "tracks/battle_graph.hpp" +#include "utils/random_generator.hpp" + +class AIProperties; +class ThreeStrikesBattle; +class BattleGraph; +class Track; +class Vec3; + +namespace irr +{ + namespace scene + { + class ISceneNode; + } +} + +class BattleAI : public AIBaseController +{ + + +protected: + + /** Keep a pointer to world. */ + ThreeStrikesBattle *m_world; + +public: + BattleAI(AbstractKart *kart, + StateManager::ActivePlayer *player=NULL); + //~BattleAI(); + virtual void update (float delta) {}; + virtual void reset () {}; + //static void enableDebug() {m_ai_debug = true; } + virtual void crashed(const AbstractKart *k) {}; + virtual void crashed(const Material *m) {}; + virtual void handleZipper(bool play_sound) {}; + virtual void finishedRace(float time) {}; + virtual void collectedItem(const Item &item, int add_info=-1, + float previous_energy=0) {}; + virtual void setPosition(int p) {}; + virtual bool isNetworkController() const { return false; } + virtual bool isPlayerController() const { return false; } + virtual void action(PlayerAction action, int value) {}; + virtual void skidBonusTriggered() {}; + virtual bool disableSlipstreamBonus() const {return 0;} + virtual void newLap(int lap) {}; +}; + +#endif \ No newline at end of file diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index e0f3c0786..55c5a476c 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -41,6 +41,10 @@ #endif #include "graphics/show_curve.hpp" #include "graphics/slip_stream.hpp" +#include "items/attachment.hpp" +#include "items/item_manager.hpp" +#include "items/powerup.hpp" +#include "items/projectile_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/controller/kart_control.hpp" #include "karts/controller/ai_properties.hpp" @@ -49,9 +53,6 @@ #include "karts/rescue_animation.hpp" #include "karts/skidding.hpp" #include "karts/skidding_properties.hpp" -#include "items/attachment.hpp" -#include "items/item_manager.hpp" -#include "items/powerup.hpp" #include "modes/linear_world.hpp" #include "modes/profile_world.hpp" #include "network/network_manager.hpp" @@ -1169,7 +1170,7 @@ void SkiddingAI::handleItems(const float dt) if( m_time_since_last_shot > 3.0f ) { - m_controls->m_fire = true; + m_controls->m_fire = true; if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_SWATTER) m_time_since_last_shot = 3.0f; else @@ -1201,29 +1202,54 @@ void SkiddingAI::handleItems(const float dt) switch( m_kart->getPowerup()->getType() ) { case PowerupManager::POWERUP_BUBBLEGUM: - // Avoid dropping all bubble gums one after another - if( m_time_since_last_shot < 3.0f) break; + { + Attachment::AttachmentType type = m_kart->getAttachment()->getType(); + // Don't use shield when we have a swatter. + if( type == Attachment::ATTACH_SWATTER || + type == Attachment::ATTACH_NOLOKS_SWATTER ) + break; - // Either use the bubble gum after 10 seconds, or if the next kart - // behind is 'close' but not too close (too close likely means that the - // kart is not behind but more to the side of this kart and so won't - // be hit by the bubble gum anyway). Should we check the speed of the - // kart as well? I.e. only drop if the kart behind is faster? Otoh - // this approach helps preventing an overtaken kart to overtake us - // again. - m_controls->m_look_back = true; //handle traditinal usage as bubble gum - m_controls->m_fire = (m_distance_behind < 15.0f && - m_distance_behind > 3.0f );//TODO: is this criteria sufficient to hit another kart with a high probability + // Check if a flyable (cake, ...) is close. If so, use bubblegum + // as shield + if( !m_kart->isShielded() && + projectile_manager->projectileIsClose(m_kart, + m_ai_properties->m_shield_incoming_radius) ) + { + m_controls->m_fire = true; + m_controls->m_look_back = false; + break; + } - m_controls->m_look_back = false; - //Do not use a shield, when you still have a swatter - if (m_kart->getAttachment()->getType() == Attachment::ATTACH_SWATTER || m_kart->getAttachment()->getType() == Attachment::ATTACH_NOLOKS_SWATTER) - break; - //else there are no objections not to use a shield - m_controls->m_fire = true; + // Avoid dropping all bubble gums one after another + if( m_time_since_last_shot < 3.0f) break; - break; // POWERUP_BUBBLEGUM + // Use bubblegum if the next kart behind is 'close' but not too close + // (too close likely means that the kart is not behind but more to the + // side of this kart and so won't be hit by the bubble gum anyway). + // Should we check the speed of the kart as well? I.e. only drop if + // the kart behind is faster? Otoh this approach helps preventing an + // overtaken kart to overtake us again. + if(m_distance_behind < 15.0f && m_distance_behind > 3.0f ) + { + m_controls->m_fire = true; + m_controls->m_look_back = true; + break; + } + // If this kart is in its last lap, drop bubble gums at every + // opportunity, since this kart won't envounter them anymore. + LinearWorld *lin_world = dynamic_cast(World::getWorld()); + if(m_time_since_last_shot > 3.0f && + lin_world && + lin_world->getKartLaps(m_kart->getWorldKartId()) + == race_manager->getNumLaps()-1) + { + m_controls->m_fire = true; + m_controls->m_look_back = true; + break; + } + break; // POWERUP_BUBBLEGUM + } // All the thrown/fired items might be improved by considering the angle // towards m_kart_ahead. case PowerupManager::POWERUP_CAKE: diff --git a/src/karts/explosion_animation.cpp b/src/karts/explosion_animation.cpp index 0720c4d0c..30a9cc30e 100644 --- a/src/karts/explosion_animation.cpp +++ b/src/karts/explosion_animation.cpp @@ -38,6 +38,11 @@ ExplosionAnimation *ExplosionAnimation::create(AbstractKart *kart, bool direct_hit) { if(kart->isInvulnerable()) return NULL; + else if(kart->isShielded()) + { + kart->decreaseShieldTime(); + return NULL; + } float r = kart->getKartProperties()->getExplosionRadius(); From 1c75270912bee636b44db239416c460f3b9ecba8 Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 25 Oct 2013 14:21:04 +0000 Subject: [PATCH 015/170] merged updates from trunk to branch git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14308 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- lib/wiiuse/CMakeLists.txt | 4 +++- lib/wiiuse/wiiuse/wiiuse.vcproj | 8 -------- lib/wiiuse/wiiuse_msvcstdint.h | 6 ++++++ src/animations/ipo.cpp | 1 + src/config/user_config.cpp | 16 ++++++++++------ src/config/user_config.hpp | 10 +++++----- src/graphics/particle_emitter.cpp | 1 + src/guiengine/layout_manager.cpp | 7 ++----- src/guiengine/skin.cpp | 1 + src/guiengine/widgets/bubble_widget.cpp | 1 + .../widgets/dynamic_ribbon_widget.cpp | 1 + src/guiengine/widgets/model_view_widget.cpp | 1 + src/guiengine/widgets/spinner_widget.cpp | 6 ++++-- src/input/wiimote.cpp | 18 ++++++++++-------- src/karts/controller/battle_ai.cpp | 17 ++++++++++++++++- src/karts/controller/battle_ai.hpp | 2 +- src/modes/three_strikes_battle.cpp | 4 +++- src/states_screens/credits.cpp | 1 + src/states_screens/options_screen_input.cpp | 1 + src/tinygettext/language.cpp | 1 + src/tracks/quad_graph.cpp | 19 ++++++++++++++++--- src/tracks/quad_graph.hpp | 4 +++- 22 files changed, 88 insertions(+), 42 deletions(-) diff --git a/lib/wiiuse/CMakeLists.txt b/lib/wiiuse/CMakeLists.txt index 36ddb6c81..e86bbd85f 100644 --- a/lib/wiiuse/CMakeLists.txt +++ b/lib/wiiuse/CMakeLists.txt @@ -1,5 +1,7 @@ # CMakeLists.txt - wiiuse +cmake_minimum_required(VERSION 2.8.1) + set(WIIUSE_SOURCES classic.c dynamics.c @@ -29,7 +31,7 @@ endif() if(MSVC) add_definitions("/DWIIUSE_STATIC") add_library(wiiuse STATIC ${WIIUSE_SOURCES}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" /MTd) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MTd") else() add_library(wiiuse ${WIIUSE_SOURCES}) endif() diff --git a/lib/wiiuse/wiiuse/wiiuse.vcproj b/lib/wiiuse/wiiuse/wiiuse.vcproj index 6f3826900..c1fccf385 100755 --- a/lib/wiiuse/wiiuse/wiiuse.vcproj +++ b/lib/wiiuse/wiiuse/wiiuse.vcproj @@ -201,10 +201,6 @@ RelativePath="..\io.h" > - - @@ -233,10 +229,6 @@ RelativePath="..\os.h" > - - diff --git a/lib/wiiuse/wiiuse_msvcstdint.h b/lib/wiiuse/wiiuse_msvcstdint.h index c66fbb817..2a11a03e6 100644 --- a/lib/wiiuse/wiiuse_msvcstdint.h +++ b/lib/wiiuse/wiiuse_msvcstdint.h @@ -102,11 +102,17 @@ typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; +// Redefinition with different type on VS 2012 +#if (_MSC_VER < 1700) typedef int16_t int_fast16_t; +#endif typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; +// Redefinition with different type on VS 2012 +#if (_MSC_VER < 1700) typedef uint16_t uint_fast16_t; +#endif typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 22433d4a4..96063cf52 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -21,6 +21,7 @@ #include "io/xml_node.hpp" #include +#include #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) # define isnan _isnan diff --git a/src/config/user_config.cpp b/src/config/user_config.cpp index a2587ba2e..48ef2c5b0 100644 --- a/src/config/user_config.cpp +++ b/src/config/user_config.cpp @@ -46,6 +46,9 @@ static PtrVector all_params; #include "utils/string_utils.hpp" #include "utils/translation.hpp" +const int UserConfig::m_current_config_version = 8; + + // ---------------------------------------------------------------------------- UserConfigParam::~UserConfigParam() { @@ -649,13 +652,14 @@ bool UserConfig::loadConfig() } // ---- Read config file version - int configFileVersion = CURRENT_CONFIG_VERSION; - if(root->get("version", &configFileVersion) < 1) + int config_file_version = m_current_config_version; + if(root->get("version", &config_file_version) < 1) { GUIEngine::showMessage( _("Your config file was malformed, so it was deleted and a new one will be created."), 10.0f); - std::cerr << "Warning, malformed user config file! Contains no version\n"; + Log::error("UserConfig", + "Warning, malformed user config file! Contains no version"); } - if (configFileVersion < CURRENT_CONFIG_VERSION) + if (config_file_version < m_current_config_version) { // current version (8) is 100% incompatible with other versions (which were lisp) // so we just delete the old config. in the future, for smaller updates, we can @@ -713,7 +717,6 @@ bool UserConfig::loadConfig() return true; } // loadConfig - // ---------------------------------------------------------------------------- /** Write settings to config file. */ void UserConfig::saveConfig() @@ -732,7 +735,8 @@ void UserConfig::saveConfig() XMLWriter configfile(filename.c_str()); configfile << L"\n"; - configfile << L"\n\n"; + configfile << L"\n\n"; const int paramAmount = all_params.size(); for(int i=0; i #include #include @@ -348,10 +346,10 @@ namespace UserConfigParams PARAM_PREFIX GroupUserConfigParam m_wiimote_group PARAM_DEFAULT( GroupUserConfigParam("WiiMote", "Settings for the wiimote") ); - PARAM_PREFIX FloatUserConfigParam m_wiimote_max - PARAM_DEFAULT( FloatUserConfigParam(90.0f, "wiimote-max", + PARAM_PREFIX FloatUserConfigParam m_wiimote_raw_max + PARAM_DEFAULT( FloatUserConfigParam(25.0f, "wiimote-raw-max", &m_wiimote_group, - "At what angle (0-128) maximum steering is reached.") ); + "At what raw input value maximum steering is reached (between 1 and 25).") ); PARAM_PREFIX FloatUserConfigParam m_wiimote_weight_linear PARAM_DEFAULT( FloatUserConfigParam(0.2f, "wiimote-weight-linear", @@ -701,6 +699,8 @@ private: std::string m_filename; irr::core::stringw m_warning; + static const int m_current_config_version; + public: /** Create the user config object; does not actually load it, * UserConfig::loadConfig needs to be called. */ diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp index 0146ccb8f..4c44ff8cf 100644 --- a/src/graphics/particle_emitter.cpp +++ b/src/graphics/particle_emitter.cpp @@ -32,6 +32,7 @@ #include #include #include +#include class FadeAwayAffector : public scene::IParticleAffector { diff --git a/src/guiengine/layout_manager.cpp b/src/guiengine/layout_manager.cpp index c917188ff..97aa43a23 100644 --- a/src/guiengine/layout_manager.cpp +++ b/src/guiengine/layout_manager.cpp @@ -60,11 +60,8 @@ int atoi_p(const char* val) bool LayoutManager::convertToCoord(std::string& x, int* absolute /* out */, int* percentage /* out */) { bool is_number; - int i; - std::istringstream myStream(x); - is_number = (myStream >> i)!=0; - - if(!is_number) return false; + int i = 0; + if (!StringUtils::fromString(x, i /* out */)) return false; if( x[x.size()-1] == '%' ) // percentage { diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index 96d5c88ad..ad19093f8 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "config/user_config.hpp" #include "graphics/irr_driver.hpp" diff --git a/src/guiengine/widgets/bubble_widget.cpp b/src/guiengine/widgets/bubble_widget.cpp index a5cc0cfe1..a68343454 100644 --- a/src/guiengine/widgets/bubble_widget.cpp +++ b/src/guiengine/widgets/bubble_widget.cpp @@ -18,6 +18,7 @@ #include "guiengine/engine.hpp" #include "guiengine/widgets/bubble_widget.hpp" #include "utils/translation.hpp" +#include #include #include diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.cpp b/src/guiengine/widgets/dynamic_ribbon_widget.cpp index 617edd8e3..0c8f7a4e2 100644 --- a/src/guiengine/widgets/dynamic_ribbon_widget.cpp +++ b/src/guiengine/widgets/dynamic_ribbon_widget.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace GUIEngine; using namespace irr::core; diff --git a/src/guiengine/widgets/model_view_widget.cpp b/src/guiengine/widgets/model_view_widget.cpp index 555a543ad..045a7e957 100644 --- a/src/guiengine/widgets/model_view_widget.cpp +++ b/src/guiengine/widgets/model_view_widget.cpp @@ -17,6 +17,7 @@ #include "guiengine/engine.hpp" #include "guiengine/widgets/model_view_widget.hpp" +#include using namespace GUIEngine; using namespace irr::core; using namespace irr::gui; diff --git a/src/guiengine/widgets/spinner_widget.cpp b/src/guiengine/widgets/spinner_widget.cpp index 8da71eec7..c762f42b9 100644 --- a/src/guiengine/widgets/spinner_widget.cpp +++ b/src/guiengine/widgets/spinner_widget.cpp @@ -64,7 +64,8 @@ void SpinnerWidget::add() { int i; std::istringstream myStream(min_s); - bool is_number = (myStream >> i)!=0; + myStream >> i; + bool is_number = (i!=0); if (is_number) { m_min = i; @@ -79,7 +80,8 @@ void SpinnerWidget::add() { int i; std::istringstream myStream(max_s); - bool is_number = (myStream >> i)!=0; + myStream >> i; + bool is_number = (i!=0); if (is_number) { m_max = i; diff --git a/src/input/wiimote.cpp b/src/input/wiimote.cpp index 67670619e..1c786619b 100644 --- a/src/input/wiimote.cpp +++ b/src/input/wiimote.cpp @@ -76,24 +76,26 @@ void Wiimote::resetIrrEvent() */ void Wiimote::update() { + float normalized_angle = -(m_wiimote_handle->accel.y-128) + / UserConfigParams::m_wiimote_raw_max; + #ifdef DEBUG if(UserConfigParams::m_wiimote_debug) { - Log::verbose("wiimote", "pitch: %f yaw %f roll %f", - m_wiimote_handle->orient.pitch, - m_wiimote_handle->orient.yaw, - m_wiimote_handle->orient.roll); + Log::verbose("wiimote", "xyz %d %d %d %f", + m_wiimote_handle->accel.x, + m_wiimote_handle->accel.y, + m_wiimote_handle->accel.z, + normalized_angle); } #endif - - float normalized_angle = -m_wiimote_handle->orient.pitch / UserConfigParams::m_wiimote_max; if(normalized_angle<-1.0f) normalized_angle = -1.0f; else if(normalized_angle>1.0f) normalized_angle = 1.0f; - // Shape the curve that determines steering depending on wiimote angle. - // The wiimote value is already normalized to be in [-1,1]. Now use a + // Shape the curve that determines steering depending on wiimote angle. + // The wiimote value is already normalized to be in [-1,1]. Now use a // weighted linear combination to compute the steering value used in game. float w1 = UserConfigParams::m_wiimote_weight_linear; float w2 = UserConfigParams::m_wiimote_weight_square; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index e14db9f77..4ff70ac00 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -21,6 +21,14 @@ #include "karts/controller/battle_ai.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/controller/kart_control.hpp" +#include "karts/controller/ai_properties.hpp" +#include "karts/kart_properties.hpp" +#include "karts/max_speed.hpp" +#include "karts/rescue_animation.hpp" +#include "karts/skidding.hpp" +#include "karts/skidding_properties.hpp" #include "modes/three_strikes_battle.hpp" @@ -43,7 +51,14 @@ BattleAI::BattleAI(AbstractKart *kart, m_track = NULL; } // Don't call our own setControllerName, since this will add a - // billboard showing 'AIBaseLapController' to the kar. + // billboard showing 'AIBaseController' to the kar. Controller::setControllerName("BattleAI"); +} + +void BattleAI::update(float dt) +{ + m_controls->m_accel = 1; + m_controls->m_steer = 0; + return; } \ No newline at end of file diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 634b044e6..0cab4eee1 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -54,7 +54,7 @@ public: BattleAI(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); //~BattleAI(); - virtual void update (float delta) {}; + virtual void update (float delta); virtual void reset () {}; //static void enableDebug() {m_ai_debug = true; } virtual void crashed(const AbstractKart *k) {}; diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 023bb4c3f..9a32c9b50 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -57,10 +57,12 @@ void ThreeStrikesBattle::init() m_display_rank = false; // check for possible problems if AI karts were incorrectly added - if(getNumKarts() > race_manager->getNumPlayers()) + // FIXME : remove this bit of code in future since ai will be added +/* if(getNumKarts() > race_manager->getNumPlayers()) { Log::fatal("Three Strikes Battle", "No AI exists for this game mode"); } +*/ m_kart_info.resize(m_karts.size()); } // ThreeStrikesBattle diff --git a/src/states_screens/credits.cpp b/src/states_screens/credits.cpp index 2504bee4e..42b7e399c 100644 --- a/src/states_screens/credits.cpp +++ b/src/states_screens/credits.cpp @@ -18,6 +18,7 @@ #include "states_screens/credits.hpp" #include +#include #include "irrString.h" using irr::core::stringw; diff --git a/src/states_screens/options_screen_input.cpp b/src/states_screens/options_screen_input.cpp index d44294465..490160abb 100644 --- a/src/states_screens/options_screen_input.cpp +++ b/src/states_screens/options_screen_input.cpp @@ -40,6 +40,7 @@ #include #include #include +#include using namespace GUIEngine; diff --git a/src/tinygettext/language.cpp b/src/tinygettext/language.cpp index 351d40be3..c3888c5bc 100644 --- a/src/tinygettext/language.cpp +++ b/src/tinygettext/language.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace tinygettext { diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 4c1e0dd31..d85b87c01 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -605,7 +605,7 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance) if(current_distancegetQuadIndex(), delta); + updateDistancesForAllSuccessors(gn->getQuadIndex(), delta, 0); } return; } @@ -634,9 +634,21 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance) * distance from start. * \param indx Index of the node for which to increase the distance. * \param delta Amount by which to increase the distance. + * \param recursive_count Counts how often this function was called + * recursively in order to catch incorrect graphs that contain loops. */ -void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta) +void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta, + unsigned int recursive_count) { + if(recursive_count>getNumNodes()) + { + Log::error("QuadGraph", + "Quad graph contains a loop (without start node)."); + Log::fatal("QuadGraph", + "Fix graph, check for directions of all shortcuts etc."); + } + recursive_count++; + GraphNode &g=getNode(indx); g.setDistanceFromStart(g.getDistanceFromStart()+delta); for(unsigned int i=0; i g_next.getDistanceFromStart()) { - updateDistancesForAllSuccessors(g.getSuccessor(i), delta); + updateDistancesForAllSuccessors(g.getSuccessor(i), delta, + recursive_count); } } } // updateDistancesForAllSuccessors diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index 7c8d5d805..228bbbf76 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -123,7 +123,9 @@ public: const video::SColor &fill_color =video::SColor(127, 255, 255, 255) ); void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const; - void updateDistancesForAllSuccessors(unsigned int indx, float delta); + void updateDistancesForAllSuccessors(unsigned int indx, + float delta, + unsigned int count); void setupPaths(); void computeChecklineRequirements(); // ---------------------------------------------------------------------- From a31c314cb446240064f67bfcad1684c329258f8b Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 29 Oct 2013 05:47:11 +0000 Subject: [PATCH 016/170] Major update to Battle AI. AI can localize itself on the navigation mesh. Battle graph is now stored as adjacency matrix instead of adjacency list. Implemented pathfinding (Floyd-Warshall), AI can now find a path from one sector to another. As a proof of concept: AI can almost follow a player kart. Some code refactoring is in order before further development. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14340 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/ai_base_controller.hpp | 6 +- .../controller/ai_base_lap_controller.hpp | 6 ++ src/karts/controller/battle_ai.cpp | 89 ++++++++++++++++++- src/karts/controller/battle_ai.hpp | 16 +++- src/modes/world.cpp | 7 ++ src/tracks/battle_graph.cpp | 54 +++++++++-- src/tracks/battle_graph.hpp | 15 +++- src/tracks/nav_poly.cpp | 6 +- src/tracks/navmesh.cpp | 2 +- 9 files changed, 178 insertions(+), 23 deletions(-) diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 0febdadbb..929e211b8 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -53,11 +53,7 @@ protected: - /** The current node the kart is on. This can be different from the value - * in LinearWorld, since it takes the chosen path of the AI into account - * (e.g. the closest point in LinearWorld might be on a branch not - * chosen by the AI). */ - int m_track_node; + virtual void setSteering (float angle, float dt); void setControllerName(const std::string &name); diff --git a/src/karts/controller/ai_base_lap_controller.hpp b/src/karts/controller/ai_base_lap_controller.hpp index 637a7e7d4..1791b5242 100644 --- a/src/karts/controller/ai_base_lap_controller.hpp +++ b/src/karts/controller/ai_base_lap_controller.hpp @@ -45,6 +45,12 @@ private: protected: + /** The current node the kart is on. This can be different from the value + * in LinearWorld, since it takes the chosen path of the AI into account + * (e.g. the closest point in LinearWorld might be on a branch not + * chosen by the AI). */ + int m_track_node; + /** Keep a pointer to world. */ LinearWorld *m_world; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 4ff70ac00..22d4ce0a1 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -30,12 +30,17 @@ #include "karts/skidding.hpp" #include "karts/skidding_properties.hpp" #include "modes/three_strikes_battle.hpp" +#include "tracks/nav_poly.hpp" +#include "tracks/navmesh.hpp" +#include BattleAI::BattleAI(AbstractKart *kart, StateManager::ActivePlayer *player) : AIBaseController(kart, player) { + + reset(); if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES) { @@ -50,6 +55,9 @@ BattleAI::BattleAI(AbstractKart *kart, m_world = NULL; m_track = NULL; } + + updateCurrentNode(); + // Don't call our own setControllerName, since this will add a // billboard showing 'AIBaseController' to the kar. Controller::setControllerName("BattleAI"); @@ -58,7 +66,82 @@ BattleAI::BattleAI(AbstractKart *kart, void BattleAI::update(float dt) { - m_controls->m_accel = 1; - m_controls->m_steer = 0; - return; + m_controls->m_accel = 0.65f; + // m_controls->m_steer = 0; + updateCurrentNode(); + + handleSteering(dt); +} + + +void BattleAI::reset() +{ + m_current_node = BattleGraph::UNKNOWN_POLY; +} + +void BattleAI::updateCurrentNode() +{ + //std::cout<<"Current Node \t"<< m_current_node << std::endl; + + // if unknown location, search everywhere + if(m_current_node == BattleGraph::UNKNOWN_POLY) + { + int max_count = BattleGraph::get()->getNumNodes(); + for(unsigned int i =0; igetPolyOfNode(i); + if(p.pointInPoly(m_kart->getXYZ())) + m_current_node = i; + } + return; + } + + if(m_current_node != BattleGraph::UNKNOWN_POLY) + { + //check if the kart is still on the same node + const NavPoly& p = BattleGraph::get()->getPolyOfNode(m_current_node); + if(p.pointInPoly(m_kart->getXYZ())) return; + + //if not then check all adjacent polys + const std::vector& adjacents = + NavMesh::get()->getAdjacentPolys(m_current_node); + + // Set m_current_node to unknown so that if no adjacent poly checks true + // we look everywhere the next time updateCurrentNode is called. This is + // useful in cases when you are "teleported" to some other poly, ex. rescue + m_current_node = BattleGraph::UNKNOWN_POLY; + + + for(unsigned int i=0; igetPolyOfNode(adjacents[i]); + if(p_temp.pointInPoly(m_kart->getXYZ())) m_current_node = adjacents[i]; + } + return; + } +} + + +void BattleAI::handleSteering(const float dt) +{ + Vec3 target_point; + const AbstractKart* kart = m_world->getPlayerKart(0); + int player_node = -1; + for(unsigned int i =0; igetNumNodes(); i++) + { + const NavPoly& p = BattleGraph::get()->getPolyOfNode(i); + if(p.pointInPoly(kart->getXYZ())) + player_node = i; + } + + if(player_node == BattleGraph::UNKNOWN_POLY || m_current_node == BattleGraph::UNKNOWN_POLY) return; + int next_node = BattleGraph::get()->getNextShortestPathPoly(m_current_node, player_node); + if(next_node == -1) return; + target_point = NavMesh::get()->getCenterOfPoly(next_node); + + if(player_node != m_current_node) + setSteering(steerToPoint(target_point),dt); + else + setSteering(steerToPoint(kart->getXYZ()),dt); } \ No newline at end of file diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 0cab4eee1..be68d0f7d 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -44,19 +44,31 @@ namespace irr class BattleAI : public AIBaseController { +private: + + int m_current_node; + + void updateCurrentNode(); + void handleAcceleration(const float dt) {}; + void handleSteering(const float dt); + void handleBraking() {}; + protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; + + public: BattleAI(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); //~BattleAI(); + unsigned int getCurrentNode() const { return m_current_node; } virtual void update (float delta); - virtual void reset () {}; - //static void enableDebug() {m_ai_debug = true; } + virtual void reset (); + virtual void crashed(const AbstractKart *k) {}; virtual void crashed(const Material *m) {}; virtual void handleZipper(bool play_sound) {}; diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 3174bb1e1..092b48d99 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -35,6 +35,7 @@ #include "io/file_manager.hpp" #include "input/device_manager.hpp" #include "items/projectile_manager.hpp" +#include "karts/controller/battle_ai.hpp" #include "karts/controller/player_controller.hpp" #include "karts/controller/end_controller.hpp" #include "karts/controller/skidding_ai.hpp" @@ -327,6 +328,9 @@ Controller* World::loadAIController(AbstractKart *kart) { Controller *controller; int turn=0; + + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES) + turn=1; // If different AIs 8should be used, adjust turn (or switch randomly // or dependent on difficulty) switch(turn) @@ -334,6 +338,9 @@ Controller* World::loadAIController(AbstractKart *kart) case 0: controller = new SkiddingAI(kart); break; + case 1: + controller = new BattleAI(kart); + break; default: Log::warn("World", "Unknown AI, using default."); controller = new SkiddingAI(kart); diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index efdf380f5..a55babc80 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -28,7 +28,7 @@ #include "tracks/navmesh.hpp" #include "utils/vec3.hpp" - +const int BattleGraph::UNKNOWN_POLY = -1; BattleGraph * BattleGraph::m_battle_graph = NULL; BattleGraph::BattleGraph(const std::string &navmesh_file_name) @@ -36,12 +36,15 @@ BattleGraph::BattleGraph(const std::string &navmesh_file_name) NavMesh::create(navmesh_file_name); m_navmesh_file = navmesh_file_name; buildGraph(NavMesh::get()); + computeFloydWarshall(); + } void BattleGraph::buildGraph(NavMesh* navmesh) { unsigned int n_polys = navmesh->getNumberOfPolys(); - m_graph.resize(n_polys); + //m_graph.resize(n_polys); + m_distance_matrix = std::vector< std::vector> (n_polys, std::vector(n_polys, 999.9f)); for(unsigned int i=0; igetNavPoly(i); @@ -50,12 +53,49 @@ void BattleGraph::buildGraph(NavMesh* navmesh) { Vec3 adjacentPolyCenter = navmesh->getCenterOfPoly(adjacents[j]); float distance = Vec3(adjacentPolyCenter - currentPoly.getCenter()).length_2d(); - m_graph[i].push_back(std::make_pair(adjacents[i], distance)); + //m_graph[i].push_back(std::make_pair(adjacents[j], distance)); + m_distance_matrix[i][adjacents[j]] = distance; + + } + m_distance_matrix[i][i] = 0.0f; + } + +} + + +// computes all pair shortest path +// iteratively updates the m_next_poly +void BattleGraph::computeFloydWarshall() +{ + int n = getNumNodes(); + + // initialize m_next_poly with unknown_poly so that if no path is found b/w i and j + // then m_next_poly[i][j] = -1 (UNKNOWN_POLY) + // AI must check this + m_next_poly = std::vector< std::vector> (n, std::vector(n,BattleGraph::UNKNOWN_POLY)); + + for(unsigned int k=0; kaddMesh(m_mesh); @@ -175,7 +215,9 @@ void BattleGraph::createDebugMesh() void BattleGraph::cleanupDebugMesh() { - irr_driver->removeNode(m_node); + if(m_node != NULL) + irr_driver->removeNode(m_node); + m_node = NULL; // No need to call irr_driber->removeMeshFromCache, since the mesh // was manually made and so never added to the mesh cache. diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp index b057df7dd..65d3a6fc6 100644 --- a/src/tracks/battle_graph.hpp +++ b/src/tracks/battle_graph.hpp @@ -43,7 +43,8 @@ private: static BattleGraph *m_battle_graph; std::vector< std::vector< std::pair > > m_graph; - + std::vector< std::vector< float > > m_distance_matrix; + std::vector< std::vector< int > > m_next_poly; /** For debug mode only: the node of the debug mesh. */ scene::ISceneNode *m_node; /** For debug only: the mesh of the debug mesh. */ @@ -56,6 +57,7 @@ private: void buildGraph(NavMesh*); + void computeFloydWarshall(); void createMesh(bool enable_transparency=false, const video::SColor *track_color=NULL); @@ -63,7 +65,9 @@ private: ~BattleGraph(void); public: - + + static const int UNKNOWN_POLY; + static BattleGraph *get() { return m_battle_graph; } static void create(const std::string &navmesh_file_name) @@ -82,7 +86,12 @@ public: } } - unsigned int getNumNodes() const { return m_graph.size(); } + unsigned int getNumNodes() const { return m_distance_matrix.size(); } + const NavPoly& getPolyOfNode(int i) const + { return NavMesh::get()->getNavPoly(i); } + + const int & getNextShortestPathPoly(int i, int j) const + { return m_next_poly[i][j]; } void createDebugMesh(); void cleanupDebugMesh(); diff --git a/src/tracks/nav_poly.cpp b/src/tracks/nav_poly.cpp index e022b3093..3670da369 100644 --- a/src/tracks/nav_poly.cpp +++ b/src/tracks/nav_poly.cpp @@ -22,7 +22,7 @@ #include "tracks/navmesh.hpp" #include - +#include NavPoly::NavPoly(const std::vector &polygonVertIndices, const std::vector &adjacentPolygonIndices) @@ -30,10 +30,10 @@ NavPoly::NavPoly(const std::vector &polygonVertIndices, m_vertices = polygonVertIndices; m_adjacents = adjacentPolygonIndices; - + std::vector xyz_points = getVertices(); - Vec3 temp; + Vec3 temp(0.0f,0.0f,0.0f); for(unsigned int i=0; i polygonVertIndices; std::vector adjacentPolygonIndices; xml_node_node->get("indices", &polygonVertIndices); - xml_node_node->get("adjacent", &adjacentPolygonIndices); + xml_node_node->get("adjacents", &adjacentPolygonIndices); NavPoly *np = new NavPoly(polygonVertIndices, adjacentPolygonIndices); m_polys.push_back(*np); m_n_polys++; From c6cbf487c96ed843987733510f7c041f73378827 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 3 Dec 2013 16:43:07 +0000 Subject: [PATCH 017/170] A new unified function ThreeStrikesBattle::updateKartNodes() that localizes all karts (AI and player) on the polygon map. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14618 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/battle_ai.cpp | 45 ++++---- src/karts/controller/battle_ai.hpp | 5 +- src/karts/controller/player_controller.cpp | 2 + src/karts/controller/player_controller.hpp | 5 + src/modes/three_strikes_battle.cpp | 119 +++++++++++++++++++++ src/modes/three_strikes_battle.hpp | 3 + 6 files changed, 158 insertions(+), 21 deletions(-) diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 22d4ce0a1..56bfa6a27 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -56,8 +56,6 @@ BattleAI::BattleAI(AbstractKart *kart, m_track = NULL; } - updateCurrentNode(); - // Don't call our own setControllerName, since this will add a // billboard showing 'AIBaseController' to the kar. Controller::setControllerName("BattleAI"); @@ -66,9 +64,9 @@ BattleAI::BattleAI(AbstractKart *kart, void BattleAI::update(float dt) { - m_controls->m_accel = 0.65f; + m_controls->m_accel = 0.45f; // m_controls->m_steer = 0; - updateCurrentNode(); + // updateCurrentNode(); handleSteering(dt); } @@ -79,22 +77,13 @@ void BattleAI::reset() m_current_node = BattleGraph::UNKNOWN_POLY; } +/* void BattleAI::updateCurrentNode() { - //std::cout<<"Current Node \t"<< m_current_node << std::endl; + std::cout<<"Current Node \t"<< m_current_node << std::endl; // if unknown location, search everywhere - if(m_current_node == BattleGraph::UNKNOWN_POLY) - { - int max_count = BattleGraph::get()->getNumNodes(); - for(unsigned int i =0; igetPolyOfNode(i); - if(p.pointInPoly(m_kart->getXYZ())) - m_current_node = i; - } - return; - } + if(m_current_node != BattleGraph::UNKNOWN_POLY) { @@ -118,10 +107,28 @@ void BattleAI::updateCurrentNode() BattleGraph::get()->getPolyOfNode(adjacents[i]); if(p_temp.pointInPoly(m_kart->getXYZ())) m_current_node = adjacents[i]; } - return; + } -} + if(m_current_node == BattleGraph::UNKNOWN_POLY) + { + int max_count = BattleGraph::get()->getNumNodes(); + //float min_dist = 9999.99f; + for(unsigned int i =0; igetPolyOfNode(i); + if((p.pointInPoly(m_kart->getXYZ()))) + { + m_current_node = i; + //min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d(); + } + } + + } + + return; +} +*/ void BattleAI::handleSteering(const float dt) { @@ -134,7 +141,7 @@ void BattleAI::handleSteering(const float dt) if(p.pointInPoly(kart->getXYZ())) player_node = i; } - + std::cout<<"PLayer node " << player_node<<" This cpu kart node" << m_current_node<getNextShortestPathPoly(m_current_node, player_node); if(next_node == -1) return; diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index be68d0f7d..420826433 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -48,7 +48,7 @@ private: int m_current_node; - void updateCurrentNode(); + //void updateCurrentNode(); void handleAcceleration(const float dt) {}; void handleSteering(const float dt); void handleBraking() {}; @@ -65,7 +65,8 @@ public: BattleAI(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); //~BattleAI(); - unsigned int getCurrentNode() const { return m_current_node; } + unsigned int getCurrentNode() const { return m_current_node; } + void setCurrentNode(int i) { m_current_node = i; } virtual void update (float delta); virtual void reset (); diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index f58b72902..d7124d6a0 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -37,6 +37,7 @@ #include "modes/world.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" +#include "tracks/battle_graph.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" @@ -93,6 +94,7 @@ void PlayerController::reset() m_prev_accel = 0; m_prev_nitro = false; m_penalty_time = 0; + m_current_node = BattleGraph::UNKNOWN_POLY; } // reset // ---------------------------------------------------------------------------- diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index cd43bcbba..74cefbe8a 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -43,6 +43,9 @@ private: float m_penalty_time; + /** This variable is required for battle mode **/ + int m_current_node; + /** The camera attached to the kart for this controller. The camera * object is managed in the Camera class, so no need to free it. */ Camera *m_camera; @@ -64,6 +67,8 @@ public: void handleZipper (bool play_sound); void collectedItem (const Item &item, int add_info=-1, float previous_energy=0); + unsigned int getCurrentNode() const { return m_current_node; } + void setCurrentNode(int i) { m_current_node = i; } virtual void skidBonusTriggered(); virtual void setPosition (int p); virtual bool isPlayerController() const {return true;} diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 9a32c9b50..264d620d3 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -26,6 +26,8 @@ #include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" #include "karts/abstract_kart.hpp" +#include "karts/controller/battle_ai.hpp" +#include "karts/controller/player_controller.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" #include "physics/physics.hpp" @@ -298,6 +300,8 @@ void ThreeStrikesBattle::update(float dt) WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); + updateKartNodes(); + core::vector3df tire_offset; std::string tire; float scale = 0.5f; @@ -432,6 +436,121 @@ bool ThreeStrikesBattle::isRaceOver() return getCurrentNumKarts()==1 || getCurrentNumPlayers()==0; } // isRaceOver +//----------------------------------------------------------------------------- +/** Updates the m_current_node value of each kart controller to localize it + * on the navigation mesh. + */ +void ThreeStrikesBattle::updateKartNodes() +{ + + const unsigned int n = getNumKarts(); + for(unsigned int i=0; iisEliminated()) continue; + + const AbstractKart* kart = m_karts[i]; + + if(!kart->getController()->isPlayerController()) + { + BattleAI* controller = (BattleAI*)(kart->getController()); + + + if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY) + { + //check if the kart is still on the same node + const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode()); + if(p.pointInPoly(kart->getXYZ())) return; + + //if not then check all adjacent polys + const std::vector& adjacents = + NavMesh::get()->getAdjacentPolys(controller->getCurrentNode()); + + // Set m_current_node to unknown so that if no adjacent poly checks true + // we look everywhere the next time updateCurrentNode is called. This is + // useful in cases when you are "teleported" to some other poly, ex. rescue + controller->setCurrentNode(BattleGraph::UNKNOWN_POLY); + + + for(unsigned int i=0; igetPolyOfNode(adjacents[i]); + if(p_temp.pointInPoly(kart->getXYZ())) + controller->setCurrentNode(adjacents[i]); + } + + } + + if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY) + { + unsigned int max_count = BattleGraph::get()->getNumNodes(); + //float min_dist = 9999.99f; + for(unsigned int i =0; igetPolyOfNode(i); + if((p.pointInPoly(kart->getXYZ()))) + { + controller->setCurrentNode(i); + //m_current_node = i; + //min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d(); + } + } + + } + + } + else + { + PlayerController* controller = (PlayerController*)(kart->getController()); + + + if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY) + { + //check if the kart is still on the same node + const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode()); + if(p.pointInPoly(kart->getXYZ())) return; + + //if not then check all adjacent polys + const std::vector& adjacents = + NavMesh::get()->getAdjacentPolys(controller->getCurrentNode()); + + // Set m_current_node to unknown so that if no adjacent poly checks true + // we look everywhere the next time updateCurrentNode is called. This is + // useful in cases when you are "teleported" to some other poly, ex. rescue + controller->setCurrentNode(BattleGraph::UNKNOWN_POLY); + + + for(unsigned int i=0; igetPolyOfNode(adjacents[i]); + if(p_temp.pointInPoly(kart->getXYZ())) + controller->setCurrentNode(adjacents[i]); + } + + } + + if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY) + { + unsigned int max_count = BattleGraph::get()->getNumNodes(); + //float min_dist = 9999.99f; + for(unsigned int i =0; igetPolyOfNode(i); + if((p.pointInPoly(kart->getXYZ()))) + { + controller->setCurrentNode(i); + //m_current_node = i; + //min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d(); + } + } + + } + } + } +} + + //----------------------------------------------------------------------------- /** Called when the race finishes, i.e. after playing (if necessary) an * end of race animation. It updates the time for all karts still racing, diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 7cb8c8eb3..dbe2a57a9 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -70,6 +70,9 @@ private: PtrVector m_tires; + /** Function to udpate the locations of all karts on the polygon map */ + void updateKartNodes(); + public: /** Used to show a nice graph when battle is over */ From fab8829cf82e2e8f97eef86e98e2e08667383e7f Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 4 Dec 2013 00:07:09 +0000 Subject: [PATCH 018/170] Declared m_ai_debug at wrong location. fixed. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14623 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/ai_base_controller.cpp | 1 + src/karts/controller/ai_base_controller.hpp | 2 ++ src/karts/controller/ai_base_lap_controller.cpp | 1 - src/karts/controller/ai_base_lap_controller.hpp | 3 +-- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index 18cc145b0..8cd7e1ed6 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -28,6 +28,7 @@ #include "tracks/track.hpp" #include "utils/constants.hpp" +bool AIBaseController::m_ai_debug = true; AIBaseController::AIBaseController(AbstractKart *kart, StateManager::ActivePlayer *player) diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 929e211b8..51f694761 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -21,6 +21,7 @@ #include "karts/controller/controller.hpp" #include "states_screens/state_manager.hpp" + class AIProperties; class LinearWorld; class ThreeStrikesBattle; @@ -60,6 +61,7 @@ protected: float steerToPoint(const Vec3 &point); float normalizeAngle(float angle); virtual bool doSkid(float steer_fraction); + static bool m_ai_debug; public: diff --git a/src/karts/controller/ai_base_lap_controller.cpp b/src/karts/controller/ai_base_lap_controller.cpp index 14aa9636d..19581f16f 100644 --- a/src/karts/controller/ai_base_lap_controller.cpp +++ b/src/karts/controller/ai_base_lap_controller.cpp @@ -29,7 +29,6 @@ #include "tracks/track.hpp" #include "utils/constants.hpp" -bool AIBaseLapController::m_ai_debug = false; /** This is the base class for all AIs. At this stage there are two similar diff --git a/src/karts/controller/ai_base_lap_controller.hpp b/src/karts/controller/ai_base_lap_controller.hpp index 1791b5242..bc5c571ea 100644 --- a/src/karts/controller/ai_base_lap_controller.hpp +++ b/src/karts/controller/ai_base_lap_controller.hpp @@ -83,8 +83,7 @@ protected: /** This can be called to detect if the kart is stuck (i.e. repeatedly * hitting part of the track). */ bool isStuck() const { return m_stuck_trigger_rescue; } - - static bool m_ai_debug; + public: AIBaseLapController(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); From 1f6e23ead93c3329c2b44312f7c15c446131e28c Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 4 Dec 2013 00:13:48 +0000 Subject: [PATCH 019/170] Implemented another debug feature: Debug spheres (red) that show where the AI kart is aiming. http://postimg.org/image/mwhbys39p/ git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14624 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/battle_ai.cpp | 40 ++++++++++++++++++++++++++++-- src/karts/controller/battle_ai.hpp | 20 ++++++++++++--- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 56bfa6a27..acd8bac77 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -18,9 +18,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#define AI_DEBUG #include "karts/controller/battle_ai.hpp" +#ifdef AI_DEBUG +# include "graphics/irr_driver.hpp" +#endif #include "karts/abstract_kart.hpp" #include "karts/controller/kart_control.hpp" #include "karts/controller/ai_properties.hpp" @@ -33,6 +37,17 @@ #include "tracks/nav_poly.hpp" #include "tracks/navmesh.hpp" +#ifdef AI_DEBUG +# include "irrlicht.h" + using namespace irr; +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) +# define isnan _isnan +#else +# include +#endif + #include BattleAI::BattleAI(AbstractKart *kart, @@ -42,6 +57,14 @@ BattleAI::BattleAI(AbstractKart *kart, reset(); + #ifdef AI_DEBUG + + video::SColor col_debug(128, 128,0,0); + m_debug_sphere = irr_driver->addSphere(1.0f, col_debug); + m_debug_sphere->setVisible(true); + //m_item_sphere = irr_driver->addSphere(1.0f); + #endif + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES) { m_world = dynamic_cast(World::getWorld()); @@ -64,14 +87,21 @@ BattleAI::BattleAI(AbstractKart *kart, void BattleAI::update(float dt) { - m_controls->m_accel = 0.45f; + m_controls->m_accel = 0.25f; // m_controls->m_steer = 0; // updateCurrentNode(); handleSteering(dt); -} +} //BattleAI +BattleAI::~BattleAI() +{ +#ifdef AI_DEBUG + irr_driver->removeNode(m_debug_sphere); +#endif +} // ~BattleAI + void BattleAI::reset() { m_current_node = BattleGraph::UNKNOWN_POLY; @@ -145,8 +175,14 @@ void BattleAI::handleSteering(const float dt) if(player_node == BattleGraph::UNKNOWN_POLY || m_current_node == BattleGraph::UNKNOWN_POLY) return; int next_node = BattleGraph::get()->getNextShortestPathPoly(m_current_node, player_node); if(next_node == -1) return; + std::cout<<"Aiming at "<getCenterOfPoly(next_node); +#ifdef AI_DEBUG + m_debug_sphere->setPosition(target_point.toIrrVector()); + Log::debug("skidding_ai","-Outside of road: steer to center point.\n"); +#endif + if(player_node != m_current_node) setSteering(steerToPoint(target_point),dt); else diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 420826433..b25e34bbf 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -47,8 +47,18 @@ class BattleAI : public AIBaseController private: int m_current_node; + + #ifdef DEBUG + + /** For debugging purpose: a sphere indicating where the AI + * is targeting at. */ + irr::scene::ISceneNode *m_debug_sphere; - //void updateCurrentNode(); + /** For item debugging: set to the item that is selected to + * be collected. */ + irr::scene::ISceneNode *m_item_sphere; + #endif + void handleAcceleration(const float dt) {}; void handleSteering(const float dt); void handleBraking() {}; @@ -59,12 +69,16 @@ protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; - +#ifdef AI_DEBUG + /** For debugging purpose: a sphere indicating where the AI + * is targeting at. */ + irr::scene::ISceneNode *m_debug_sphere; +#endif public: BattleAI(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); - //~BattleAI(); + ~BattleAI(); unsigned int getCurrentNode() const { return m_current_node; } void setCurrentNode(int i) { m_current_node = i; } virtual void update (float delta); From c23f9621195d504581148904add6be0587d3e4fc Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 4 Dec 2013 13:33:36 +0000 Subject: [PATCH 020/170] Reimplemented the Floyd-Warshall path reconstruction to fix a bug where the next poly returned would actually be a poly close to the destination instead of being close to source polygon. This ensures AI follows right path and gets stuck less often. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14630 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/battle_ai.cpp | 22 +++++++++----- src/tracks/battle_graph.cpp | 47 +++++++++++++++++++++--------- src/tracks/battle_graph.hpp | 4 +-- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index acd8bac77..482b87506 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -87,7 +87,7 @@ BattleAI::BattleAI(AbstractKart *kart, void BattleAI::update(float dt) { - m_controls->m_accel = 0.25f; + m_controls->m_accel = 0.5f; // m_controls->m_steer = 0; // updateCurrentNode(); @@ -173,18 +173,24 @@ void BattleAI::handleSteering(const float dt) } std::cout<<"PLayer node " << player_node<<" This cpu kart node" << m_current_node<getXYZ(); + setSteering(steerToPoint(kart->getXYZ()),dt); + std::cout<<"Aiming at sire nixt\n"; + } + else + { int next_node = BattleGraph::get()->getNextShortestPathPoly(m_current_node, player_node); - if(next_node == -1) return; + std::cout<<"Aiming at "<getCenterOfPoly(next_node); - + setSteering(steerToPoint(target_point),dt); + } #ifdef AI_DEBUG m_debug_sphere->setPosition(target_point.toIrrVector()); Log::debug("skidding_ai","-Outside of road: steer to center point.\n"); #endif - - if(player_node != m_current_node) - setSteering(steerToPoint(target_point),dt); - else - setSteering(steerToPoint(kart->getXYZ()),dt); + } \ No newline at end of file diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index a55babc80..f4af94190 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -17,7 +17,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, B #include "tracks/battle_graph.hpp" - +#include #include #include @@ -44,7 +44,7 @@ void BattleGraph::buildGraph(NavMesh* navmesh) { unsigned int n_polys = navmesh->getNumberOfPolys(); //m_graph.resize(n_polys); - m_distance_matrix = std::vector< std::vector> (n_polys, std::vector(n_polys, 999.9f)); + m_distance_matrix = std::vector< std::vector> (n_polys, std::vector(n_polys, 9999.9f)); for(unsigned int i=0; igetNavPoly(i); @@ -56,25 +56,45 @@ void BattleGraph::buildGraph(NavMesh* navmesh) //m_graph[i].push_back(std::make_pair(adjacents[j], distance)); m_distance_matrix[i][adjacents[j]] = distance; + //m_distance_matrix[adjacents[j]][i] = distance; } m_distance_matrix[i][i] = 0.0f; } - + + for(int i=0; i0.1) + { + int d; + std::cout<<"messed up"; + std::cin>>d; + } +} } - -// computes all pair shortest path -// iteratively updates the m_next_poly +/** 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 + */ void BattleGraph::computeFloydWarshall() { int n = getNumNodes(); - // initialize m_next_poly with unknown_poly so that if no path is found b/w i and j - // then m_next_poly[i][j] = -1 (UNKNOWN_POLY) + // initialize m_parent_poly with unknown_poly so that if no path is found b/w i and j + // then m_parent_poly[i][j] = -1 (UNKNOWN_POLY) // AI must check this - m_next_poly = std::vector< std::vector> (n, std::vector(n,BattleGraph::UNKNOWN_POLY)); - + 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(unsigned int k=0; k > > m_graph; std::vector< std::vector< float > > m_distance_matrix; - std::vector< std::vector< int > > m_next_poly; + std::vector< std::vector< int > > m_parent_poly; /** For debug mode only: the node of the debug mesh. */ scene::ISceneNode *m_node; /** For debug only: the mesh of the debug mesh. */ @@ -91,7 +91,7 @@ public: { return NavMesh::get()->getNavPoly(i); } const int & getNextShortestPathPoly(int i, int j) const - { return m_next_poly[i][j]; } + { return m_parent_poly[j][i]; } void createDebugMesh(); void cleanupDebugMesh(); From 96d5666a08cb7a6658f4e0b52b33bdaed8f0059a Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 6 Dec 2013 17:16:35 +0000 Subject: [PATCH 021/170] Code refactoring: Moved AIBaseLapController::crashed()and its associated vars to AIBaseController::crashed(). Created update() and reset() in AIBaseController which are now called from AIBaseLapController's respective update/reset. Hopefully I didn't break the skidding AI. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14646 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/ai_base_controller.cpp | 77 ++++++++++++++++++- src/karts/controller/ai_base_controller.hpp | 23 +++++- .../controller/ai_base_lap_controller.cpp | 68 +--------------- .../controller/ai_base_lap_controller.hpp | 14 +--- 4 files changed, 100 insertions(+), 82 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index 8cd7e1ed6..53c2649d1 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -25,10 +25,11 @@ #include "karts/kart_properties.hpp" #include "karts/skidding_properties.hpp" #include "karts/controller/ai_properties.hpp" +#include "modes/world.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" -bool AIBaseController::m_ai_debug = true; +bool AIBaseController::m_ai_debug = false; AIBaseController::AIBaseController(AbstractKart *kart, StateManager::ActivePlayer *player) @@ -42,6 +43,18 @@ AIBaseController::AIBaseController(AbstractKart *kart, } +void AIBaseController::reset() +{ + m_stuck = false; + m_collision_times.clear(); +} // reset + +void AIBaseController::update(float dt) +{ + m_stuck = false; +} + + //----------------------------------------------------------------------------- /** In debug mode when the user specified --ai-debug on the command line set * the name of the controller as on-screen text, so that the different AI @@ -233,4 +246,64 @@ bool AIBaseController::doSkid(float steer_fraction) // Otherwise return if we need a sharp turn (which is // for the old skidding implementation). return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold; -} // doSkid \ No newline at end of file +} // doSkid + + +//----------------------------------------------------------------------------- +/** This is called when the kart crashed with the terrain. This subroutine + * tries to detect if the AI is stuck by determining if a certain number + * of collisions happened in a certain amount of time, and if so rescues + * the kart. + * \paran m Pointer to the material that was hit (NULL if no specific + * material was used for the part of the track that was hit). + */ +void AIBaseController::crashed(const Material *m) +{ + // Defines how many collision in what time will trigger a rescue. + // Note that typically it takes ~0.5 seconds for the AI to hit + // the track again if it is stuck (i.e. time for the push back plus + // time for the AI to accelerate and hit the terrain again). + const unsigned int NUM_COLLISION = 3; + const float COLLISION_TIME = 1.5f; + + float time = World::getWorld()->getTime(); + if(m_collision_times.size()==0) + { + m_collision_times.push_back(time); + return; + } + + // Filter out multiple collisions report caused by single collision + // (bullet can report a collision more than once per frame, and + // resolving it can take a few frames as well, causing more reported + // collisions to happen). The time of 0.2 seconds was experimentally + // found, typically it takes 0.5 seconds for a kart to be pushed back + // from the terrain and accelerate to hit the same terrain again. + if(time - m_collision_times.back() < 0.2f) + return; + + + // Remove all outdated entries, i.e. entries that are older than the + // collision time plus 1 second. Older entries must be deleted, + // otherwise a collision that happened (say) 10 seconds ago could + // contribute to a stuck condition. + while(m_collision_times.size()>0 && + time - m_collision_times[0] > 1.0f+COLLISION_TIME) + m_collision_times.erase(m_collision_times.begin()); + + m_collision_times.push_back(time); + + // Now detect if there are enough collision records in the + // specified time interval. + if(time - m_collision_times.front() > COLLISION_TIME + && m_collision_times.size()>=NUM_COLLISION) + { + // We can't call m_kart->forceRescue here, since crased() is + // called during physics processing, and forceRescue() removes the + // chassis from the physics world, which would then cause + // inconsistencies and potentially a crash during the physics + // processing. So only set a flag, which is tested during update. + m_stuck = true; + } + +} // crashed(Material) diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 51f694761..6d0a0de3a 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -33,7 +33,15 @@ class Vec3; class AIBaseController : public Controller { +private: + /** Stores the last N times when a collision happened. This is used + * to detect when the AI is stuck, i.e. N collisions happened in + * a certain period of time. */ + std::vector m_collision_times; + /** A flag that is set during the physics processing to indicate that + * this kart is stuck and needs to be rescued. */ + bool m_stuck; protected: @@ -53,23 +61,32 @@ protected: const AIProperties *m_ai_properties; - + static bool m_ai_debug; - + virtual void update (float delta) ; virtual void setSteering (float angle, float dt); void setControllerName(const std::string &name); float steerToPoint(const Vec3 &point); float normalizeAngle(float angle); virtual bool doSkid(float steer_fraction); - static bool m_ai_debug; + + + + // ------------------------------------------------------------------------ + /** This can be called to detect if the kart is stuck (i.e. repeatedly + * hitting part of the track). */ + bool isStuck() const { return m_stuck; } public: AIBaseController(AbstractKart *kart, StateManager::ActivePlayer *player=NULL); virtual ~AIBaseController() {}; + virtual void reset(); virtual bool disableSlipstreamBonus() const; + + virtual void crashed(const Material *m); }; #endif \ No newline at end of file diff --git a/src/karts/controller/ai_base_lap_controller.cpp b/src/karts/controller/ai_base_lap_controller.cpp index 19581f16f..3660869e0 100644 --- a/src/karts/controller/ai_base_lap_controller.cpp +++ b/src/karts/controller/ai_base_lap_controller.cpp @@ -113,8 +113,7 @@ AIBaseLapController::AIBaseLapController(AbstractKart *kart, //----------------------------------------------------------------------------- void AIBaseLapController::reset() { - m_stuck_trigger_rescue = false; - m_collision_times.clear(); + AIBaseController::reset(); } // reset @@ -195,14 +194,13 @@ void AIBaseLapController::computePath() //----------------------------------------------------------------------------- /** Updates the ai base controller each time step. Note that any calls to - * isStuck() must be done before update is called, since update will reset - * the isStuck flag! + * isStuck() must be done before update is called, since update will call + * AIBaseController::update() which will reset the isStuck flag! * \param dt Time step size. */ void AIBaseLapController::update(float dt) { - m_stuck_trigger_rescue = false; - + AIBaseController::update(dt); if(QuadGraph::get()) { // Update the current node: @@ -226,64 +224,6 @@ void AIBaseLapController::update(float dt) } } // update -//----------------------------------------------------------------------------- -/** This is called when the kart crashed with the terrain. This subroutine - * tries to detect if the AI is stuck by determining if a certain number - * of collisions happened in a certain amount of time, and if so rescues - * the kart. - * \paran m Pointer to the material that was hit (NULL if no specific - * material was used for the part of the track that was hit). - */ -void AIBaseLapController::crashed(const Material *m) -{ - // Defines how many collision in what time will trigger a rescue. - // Note that typically it takes ~0.5 seconds for the AI to hit - // the track again if it is stuck (i.e. time for the push back plus - // time for the AI to accelerate and hit the terrain again). - const unsigned int NUM_COLLISION = 3; - const float COLLISION_TIME = 1.5f; - - float time = World::getWorld()->getTime(); - if(m_collision_times.size()==0) - { - m_collision_times.push_back(time); - return; - } - - // Filter out multiple collisions report caused by single collision - // (bullet can report a collision more than once per frame, and - // resolving it can take a few frames as well, causing more reported - // collisions to happen). The time of 0.2 seconds was experimentally - // found, typically it takes 0.5 seconds for a kart to be pushed back - // from the terrain and accelerate to hit the same terrain again. - if(time - m_collision_times.back() < 0.2f) - return; - - - // Remove all outdated entries, i.e. entries that are older than the - // collision time plus 1 second. Older entries must be deleted, - // otherwise a collision that happened (say) 10 seconds ago could - // contribute to a stuck condition. - while(m_collision_times.size()>0 && - time - m_collision_times[0] > 1.0f+COLLISION_TIME) - m_collision_times.erase(m_collision_times.begin()); - - m_collision_times.push_back(time); - - // Now detect if there are enough collision records in the - // specified time interval. - if(time - m_collision_times.front() > COLLISION_TIME - && m_collision_times.size()>=NUM_COLLISION) - { - // We can't call m_kart->forceRescue here, since crased() is - // called during physics processing, and forceRescue() removes the - // chassis from the physics world, which would then cause - // inconsistencies and potentially a crash during the physics - // processing. So only set a flag, which is tested during update. - m_stuck_trigger_rescue = true; - } - -} // crashed(Material) //----------------------------------------------------------------------------- /** Returns the next sector of the given sector index. This is used diff --git a/src/karts/controller/ai_base_lap_controller.hpp b/src/karts/controller/ai_base_lap_controller.hpp index bc5c571ea..ad565d61b 100644 --- a/src/karts/controller/ai_base_lap_controller.hpp +++ b/src/karts/controller/ai_base_lap_controller.hpp @@ -33,15 +33,7 @@ class Vec3; */ class AIBaseLapController : public AIBaseController { -private: - /** Stores the last N times when a collision happened. This is used - * to detect when the AI is stuck, i.e. N collisions happened in - * a certain period of time. */ - std::vector m_collision_times; - /** A flag that is set during the physics processing to indicate that - * this kart is stuck and needs to be rescued. */ - bool m_stuck_trigger_rescue; protected: @@ -79,10 +71,7 @@ protected: // ------------------------------------------------------------------------ /** Nothing special to do when the race is finished. */ virtual void raceFinished() {}; - // ------------------------------------------------------------------------ - /** This can be called to detect if the kart is stuck (i.e. repeatedly - * hitting part of the track). */ - bool isStuck() const { return m_stuck_trigger_rescue; } + public: AIBaseLapController(AbstractKart *kart, @@ -91,7 +80,6 @@ public: virtual void reset(); static void enableDebug() {m_ai_debug = true; } virtual void crashed(const AbstractKart *k) {}; - virtual void crashed(const Material *m); virtual void handleZipper(bool play_sound) {}; virtual void finishedRace(float time) {}; virtual void collectedItem(const Item &item, int add_info=-1, From 20d30bc09d9ddc148c839cedd2480e7903aec400 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 8 Dec 2013 00:00:20 +0000 Subject: [PATCH 022/170] BattleAI: Karts can now get un-stuck. They will reverse while turning in the opposite direction. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14653 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/battle_ai.cpp | 149 ++++++++++++++++++++--------- src/karts/controller/battle_ai.hpp | 11 ++- 2 files changed, 110 insertions(+), 50 deletions(-) diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 482b87506..3ef553d1d 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -85,16 +85,6 @@ BattleAI::BattleAI(AbstractKart *kart, } -void BattleAI::update(float dt) -{ - m_controls->m_accel = 0.5f; - // m_controls->m_steer = 0; - // updateCurrentNode(); - - handleSteering(dt); -} //BattleAI - - BattleAI::~BattleAI() { #ifdef AI_DEBUG @@ -105,8 +95,110 @@ BattleAI::~BattleAI() void BattleAI::reset() { m_current_node = BattleGraph::UNKNOWN_POLY; + m_time_since_stuck = 0.0f; + m_currently_reversing = false; + AIBaseController::reset(); } +void BattleAI::update(float dt) +{ + m_controls->m_accel = 0.5f; + // m_controls->m_steer = 0; + // updateCurrentNode(); + + handleSteering(dt); + handleGetUnstuck(dt); + AIBaseController::update(dt); +} //BattleAI + + +void BattleAI::handleGetUnstuck(const float dt) +{ + if(isStuck() == true) + { + std::cout<<"GOT STUCK\n"; + m_time_since_stuck = 0.0f; + m_currently_reversing = true; + } + if(m_currently_reversing == true) + { + m_controls->m_accel = -0.30f; + setSteering(-1.0f*m_target_angle,dt); + m_time_since_stuck += dt; + + if(m_time_since_stuck >= 0.6f) + { + m_currently_reversing = false; + m_time_since_stuck = 0.0f; + } + } +} + + + +// Handles steering. Returns the steering angle being used for setSteering() +void BattleAI::handleSteering(const float dt) +{ + Vec3 target_point; + const AbstractKart* kart = m_world->getPlayerKart(0); + int player_node = -1; + for(unsigned int i =0; igetNumNodes(); i++) + { + const NavPoly& p = BattleGraph::get()->getPolyOfNode(i); + if(p.pointInPoly(kart->getXYZ())) + player_node = i; + } + // std::cout<<"PLayer node " << player_node<<" This cpu kart node" << m_current_node<getXYZ(); + std::cout<<"Aiming at sire nixt\n"; + } + else + { + int next_node = BattleGraph::get()->getNextShortestPathPoly(m_current_node, player_node); + + // std::cout<<"Aiming at "<getCenterOfPoly(next_node); + } + m_target_angle = steerToPoint(target_point); + setSteering(m_target_angle,dt); + +#ifdef AI_DEBUG + m_debug_sphere->setPosition(target_point.toIrrVector()); + Log::debug("skidding_ai","-Outside of road: steer to center point.\n"); +#endif + +} + + + +/* +float BattleAI::isStuck(const float dt) +{ + // check if kart is stuck + if(m_kart->getSpeed()<2.0f && !m_kart->getKartAnimation() && + !m_world->isStartPhase()) + { + m_time_since_stuck += dt; + if(m_time_since_stuck > 2.0f) + { + return true; + m_time_since_stuck=0.0f; + } // m_time_since_stuck > 2.0f + } + else + { + m_time_since_stuck = 0.0f; + return false; + } +} + +*/ + + /* void BattleAI::updateCurrentNode() { @@ -158,39 +250,4 @@ void BattleAI::updateCurrentNode() return; } -*/ - -void BattleAI::handleSteering(const float dt) -{ - Vec3 target_point; - const AbstractKart* kart = m_world->getPlayerKart(0); - int player_node = -1; - for(unsigned int i =0; igetNumNodes(); i++) - { - const NavPoly& p = BattleGraph::get()->getPolyOfNode(i); - if(p.pointInPoly(kart->getXYZ())) - player_node = i; - } - std::cout<<"PLayer node " << player_node<<" This cpu kart node" << m_current_node<getXYZ(); - setSteering(steerToPoint(kart->getXYZ()),dt); - std::cout<<"Aiming at sire nixt\n"; - } - else - { - int next_node = BattleGraph::get()->getNextShortestPathPoly(m_current_node, player_node); - - std::cout<<"Aiming at "<getCenterOfPoly(next_node); - setSteering(steerToPoint(target_point),dt); - } -#ifdef AI_DEBUG - m_debug_sphere->setPosition(target_point.toIrrVector()); - Log::debug("skidding_ai","-Outside of road: steer to center point.\n"); -#endif - -} \ No newline at end of file +*/ \ No newline at end of file diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index b25e34bbf..f9805b432 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -47,9 +47,13 @@ class BattleAI : public AIBaseController private: int m_current_node; - + + float m_target_angle; + + float m_time_since_stuck; + bool m_currently_reversing; + #ifdef DEBUG - /** For debugging purpose: a sphere indicating where the AI * is targeting at. */ irr::scene::ISceneNode *m_debug_sphere; @@ -62,7 +66,7 @@ private: void handleAcceleration(const float dt) {}; void handleSteering(const float dt); void handleBraking() {}; - + void handleGetUnstuck(const float dt); protected: @@ -85,7 +89,6 @@ public: virtual void reset (); virtual void crashed(const AbstractKart *k) {}; - virtual void crashed(const Material *m) {}; virtual void handleZipper(bool play_sound) {}; virtual void finishedRace(float time) {}; virtual void collectedItem(const Item &item, int add_info=-1, From a49a990a99713ca7f5f0c63c07cd091c6625d984 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 8 Dec 2013 00:11:40 +0000 Subject: [PATCH 023/170] Improved kart localization on the nav map. The old location is mainted when a kart moves out of the nav map and cannot be found on it. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14654 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/modes/three_strikes_battle.cpp | 166 +++++++++++++++-------------- 1 file changed, 86 insertions(+), 80 deletions(-) diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 264d620d3..19255b2c6 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -452,100 +452,106 @@ void ThreeStrikesBattle::updateKartNodes() if(!kart->getController()->isPlayerController()) { - BattleAI* controller = (BattleAI*)(kart->getController()); + BattleAI* controller = (BattleAI*)(kart->getController()); + int saved_current_node = controller->getCurrentNode(); - if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY) + if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY) + { + //check if the kart is still on the same node + const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode()); + if(p.pointInPoly(kart->getXYZ())) return; + + //if not then check all adjacent polys + const std::vector& adjacents = + NavMesh::get()->getAdjacentPolys(controller->getCurrentNode()); + + // Set m_current_node to unknown so that if no adjacent poly checks true + // we look everywhere the next time updateCurrentNode is called. This is + // useful in cases when you are "teleported" to some other poly, ex. rescue + controller->setCurrentNode(BattleGraph::UNKNOWN_POLY); + + + for(unsigned int i=0; igetPolyOfNode(controller->getCurrentNode()); - if(p.pointInPoly(kart->getXYZ())) return; - - //if not then check all adjacent polys - const std::vector& adjacents = - NavMesh::get()->getAdjacentPolys(controller->getCurrentNode()); - - // Set m_current_node to unknown so that if no adjacent poly checks true - // we look everywhere the next time updateCurrentNode is called. This is - // useful in cases when you are "teleported" to some other poly, ex. rescue - controller->setCurrentNode(BattleGraph::UNKNOWN_POLY); - - - for(unsigned int i=0; igetPolyOfNode(adjacents[i]); - if(p_temp.pointInPoly(kart->getXYZ())) - controller->setCurrentNode(adjacents[i]); - } - - } - - if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY) - { - unsigned int max_count = BattleGraph::get()->getNumNodes(); - //float min_dist = 9999.99f; - for(unsigned int i =0; igetPolyOfNode(i); - if((p.pointInPoly(kart->getXYZ()))) - { - controller->setCurrentNode(i); - //m_current_node = i; - //min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d(); - } - } - + const NavPoly& p_temp = + BattleGraph::get()->getPolyOfNode(adjacents[i]); + if(p_temp.pointInPoly(kart->getXYZ())) + controller->setCurrentNode(adjacents[i]); } + + } + + if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY) + { + bool flag = 0; + unsigned int max_count = BattleGraph::get()->getNumNodes(); + //float min_dist = 9999.99f; + for(unsigned int i =0; igetPolyOfNode(i); + if((p.pointInPoly(kart->getXYZ()))) + { + controller->setCurrentNode(i); + flag = 1; + //min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d(); + } + } + + if(flag == 0) controller->setCurrentNode(saved_current_node); + } } else { PlayerController* controller = (PlayerController*)(kart->getController()); + int saved_current_node = controller->getCurrentNode(); - if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY) + if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY) + { + //check if the kart is still on the same node + const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode()); + if(p.pointInPoly(kart->getXYZ())) return; + + //if not then check all adjacent polys + const std::vector& adjacents = + NavMesh::get()->getAdjacentPolys(controller->getCurrentNode()); + + // Set m_current_node to unknown so that if no adjacent poly checks true + // we look everywhere the next time updateCurrentNode is called. This is + // useful in cases when you are "teleported" to some other poly, ex. rescue + controller->setCurrentNode(BattleGraph::UNKNOWN_POLY); + + + for(unsigned int i=0; igetPolyOfNode(controller->getCurrentNode()); - if(p.pointInPoly(kart->getXYZ())) return; - - //if not then check all adjacent polys - const std::vector& adjacents = - NavMesh::get()->getAdjacentPolys(controller->getCurrentNode()); - - // Set m_current_node to unknown so that if no adjacent poly checks true - // we look everywhere the next time updateCurrentNode is called. This is - // useful in cases when you are "teleported" to some other poly, ex. rescue - controller->setCurrentNode(BattleGraph::UNKNOWN_POLY); - - - for(unsigned int i=0; igetPolyOfNode(adjacents[i]); - if(p_temp.pointInPoly(kart->getXYZ())) - controller->setCurrentNode(adjacents[i]); - } - - } - - if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY) - { - unsigned int max_count = BattleGraph::get()->getNumNodes(); - //float min_dist = 9999.99f; - for(unsigned int i =0; igetPolyOfNode(i); - if((p.pointInPoly(kart->getXYZ()))) - { - controller->setCurrentNode(i); - //m_current_node = i; - //min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d(); - } - } + const NavPoly& p_temp = + BattleGraph::get()->getPolyOfNode(adjacents[i]); + if(p_temp.pointInPoly(kart->getXYZ())) + controller->setCurrentNode(adjacents[i]); + } } + + if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY) + { + bool flag = 0; + unsigned int max_count = BattleGraph::get()->getNumNodes(); + //float min_dist = 9999.99f; + for(unsigned int i =0; igetPolyOfNode(i); + if((p.pointInPoly(kart->getXYZ()))) + { + controller->setCurrentNode(i); + flag = 1; + //min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d(); + } + } + + if(flag == 0) controller->setCurrentNode(saved_current_node); + } } } } From 1c8dfb72e68a55ad671ac46baa27801d811d8a52 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 8 Dec 2013 03:35:17 +0000 Subject: [PATCH 024/170] Last commit failed due to permission error. Testing to see if this commit succeeds. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14658 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/CREDITS | Bin 11530 -> 12854 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/CREDITS b/data/CREDITS index cd9fd22eab44f25bcc1677ed287adbc441719dc5..92fc11536754fb925aede34a4975915c5216eb2d 100644 GIT binary patch delta 3275 zcmZ`*OH5o<7(T|l#c(@IMaXbTi*Ne4^Ym{6GEF=d#U+~HB$ z1yQ?lqwT+OVbXNx!l+1NV!F|VHExW?7~_gAG~KyzVVaoK@B7cafKhXE?>&$I@%_(p z-nsU9`n}KYe%UadGt*|#1WmhfOwufw0_Lt6!|0|NGLslDVkcvY_`2B3;+@27+N80v zWODeE!!iA4)C}W)%)D%luKzY2khzl4ieaTE?W}xJ5)UO+<|O9x9LY~5pL7kH1?Y86 z-Yk1^hoNy6n&+^#q}m)%QoL#X79bwP#%OKU9uTzT64@H)1%<$ zAiyA zLr*y7C_H}>Z-=Mv3^-S?N3Zo`OqxR)FZ!7e-O`D77Gq|D3o06#*^Y>`S5EyGv-S|by8aaF=i;BS0Yv6HSW+FIzHo} zp$!@6VAlEAn1>5^bD-M}AGwRLnJel+%u}9D$FyMPBglF_8o}-y=oj!znshHndf3VJlv$?u3}UKkldEr{?j-K)zq!I2!eHOFMv;J#u5B1W|US`vsq4Q4@f`8G>1Vt zqlTUbG^WR#&JLN^F{4}PwwrkT;PvZ@Esjx)5r%6JVk>GE3ycY~qzJ}kHNelvE2@&U zZo&2dP8!Bvger0{rdiLEG#q>~Q9iiwr2Jt?WK8v@P{Mi|LZan9?n@B%0C?#%BS;hL#4hF-?0O9D}Fw8b<2B z2nQ5S0*=qlJhPe0KF6GO@%5ooaTM>wsFEAWHwbU%z!<_4Va!UL9uZFUCFDkSl2=~LWH@hOkuf(Aa1 z0JCaGfhvt;V2mus81{YIaja*})Gn~QCL!yUwK7(fXtNDw$=m{LR*nzK=Aq3J8rCW6 zRnaC=pGAJKmy_}zCdy{j5S(~8bgF}N{pJGd%>tPOjA=EG8oUHRmq-H?t9e!Ok+3`t zRfGTMP>1ZTJ`YaR^I~& zbRZpJ4{Gm#mYTA587+HDzHexddj}e|iq;PtuVa?9B7n@$Rm0{ug?T~(^=;Bye@BAh z{Tq+#cL(IP@R)qp&?xVRKb7l^A2(eFDYdg~Sa3X8wvJu-vn*1{NNlC!Mn}`r68XRx zSnI+vTl@@Ic&4zE`qzkmuiNv)h5F{PteK|PAOhp9jp=#|V|Mj90H+hV$9(l~Y<`(Cx~RLCutA?#GG3VH1J-vo zt6YE~Ghj$}yh*P(y$XX~qB--JEvphniI=EVm39`&?SaiZ!3{_$=-jqUdY@)y!Py7s z>`g3P;;<&O7N@~sO}>ObI@wVy(zQP1BuHGO6I+};US~l@Mw___dFFJN68wp1Qy?MZ z(G&RIjkf99qi6)PKcap3zSE0B`MvoS%wn+~d{eO@e7}jk zTMDRh^`MpX_Q9BZbnw!>_#4=qh_}n>(1A*3A%W7)gnS!+guTs3Og?Nm9Y}0EZHevr E8|!{+9smFU delta 1987 zcmZuyU1*zS6h3MC=hq}lnsv7QlJ?7j{gSk4Eo-em9kuo+wKKCdbDM%l)}}v8(v18# z>xC6$3^oMiTKt#~F3)zL}g+EtG?NrxVNZ`=NvssV>I^G$9h+ZHfWS~C`09H*pYDr<7Ch*6=7x2I&I@C zLu*)VQ-OB)V#}%{!QZy~`OlVaHk|KsZ)>ijL~(U`xmD+D{Ubhuj*^BaNfj!=bB(|2 ze?6sP*PxTI#12pmO54agw277Fa}uXn#48}OWKvNXJ>#qAdEaE2L zat&HoVH12LY8f)65qlkIO?h#fd&~(>o^lWKywl5HIz2q?Ik=aCeL-a)@}{AYsd&uU zRnI`brY-??U25U5f`Y80FjC6KGmTCFd*A;Q&U307(up?0o((PyCKX=7D->j0>OnM` zvqde~_EL8lrwzPPUW5J@-nKUBG@4wU~G`w2|kv-q_M z@^x=tpX|>nN)>oDcHKWA-M6TEBCiyd*^aZ*H-29o;fQZxMi7>e#mh>{1_bP{scE%_ z(aN-$bD7_E2X(=i1Jjoj&(m1RNXgixIj!}CmLQqj0iuG~O6O&ft=uP~jn&&BQM)WrEGJ<5Lv1MCXCR=pbNbf`rxFcGve6S=H$ zlk(K^O!*qeF(Fo)tiIAw(RZaoIpN#yHQl4j-6N7qfjS9QPTLG!%6-z z+{;^Fhs;6#Aspa&^KrF%!3^;=b3lFHG85|8A9(h!%>9IHKV5C0Z(ISl?j X9OS=ZpYqwmhIkFnh*i};5^nzwmJN~P From 43f1c59a6e0195822cc2c5ad623d5ab5af2accf4 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 8 Dec 2013 03:45:57 +0000 Subject: [PATCH 025/170] Huge merge from trunk, part 1. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14659 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/Makefile.am | 29 +- src/achievements/achievement.cpp | 209 + src/achievements/achievement.hpp | 101 + src/achievements/achievement_info.cpp | 87 + src/achievements/achievement_info.hpp | 87 + src/achievements/achievements_manager.cpp | 247 + src/achievements/achievements_manager.hpp | 70 + src/achievements/achievements_slot.cpp | 153 + src/achievements/achievements_slot.hpp | 64 + src/addons/addon.cpp | 2 +- src/addons/addon.hpp | 7 +- src/addons/addons_manager.cpp | 34 +- src/addons/addons_manager.hpp | 2 +- src/addons/dummy_network_http.hpp | 2 +- src/addons/inetwork_http.cpp | 2 +- src/addons/inetwork_http.hpp | 4 +- src/addons/network_http.cpp | 39 +- src/addons/network_http.hpp | 6 +- src/addons/news_manager.cpp | 2 +- src/addons/news_manager.hpp | 2 +- src/addons/request.cpp | 2 +- src/addons/request.hpp | 2 +- src/addons/zip.cpp | 2 +- src/addons/zip.hpp | 2 +- src/animations/animation_base.cpp | 10 +- src/animations/animation_base.hpp | 2 +- src/animations/ipo.cpp | 10 +- src/animations/ipo.hpp | 2 +- src/animations/three_d_animation.cpp | 2 +- src/animations/three_d_animation.hpp | 2 +- src/audio/dummy_sfx.hpp | 4 +- src/audio/music.hpp | 2 +- src/audio/music_dummy.hpp | 2 +- src/audio/music_information.cpp | 2 +- src/audio/music_information.hpp | 2 +- src/audio/music_manager.cpp | 4 +- src/audio/music_manager.hpp | 4 +- src/audio/music_ogg.cpp | 2 +- src/audio/music_ogg.hpp | 2 +- src/audio/sfx_base.hpp | 4 +- src/audio/sfx_buffer.cpp | 5 +- src/audio/sfx_buffer.hpp | 2 +- src/audio/sfx_manager.cpp | 4 +- src/audio/sfx_manager.hpp | 2 +- src/audio/sfx_openal.cpp | 13 +- src/audio/sfx_openal.hpp | 2 +- src/challenges/challenge.cpp | 14 +- src/challenges/challenge.hpp | 2 +- src/challenges/challenge_data.cpp | 2 +- src/challenges/challenge_data.hpp | 2 +- src/challenges/game_slot.cpp | 2 +- src/challenges/game_slot.hpp | 2 +- src/challenges/unlock_manager.cpp | 16 +- src/challenges/unlock_manager.hpp | 5 +- src/config/device_config.cpp | 2 +- src/config/device_config.hpp | 2 +- src/config/player.cpp | 2 +- src/config/player.hpp | 2 +- src/config/saved_grand_prix.cpp | 2 +- src/config/saved_grand_prix.hpp | 2 +- src/config/stk_config.cpp | 48 +- src/config/stk_config.hpp | 8 +- src/config/user_config.cpp | 169 +- src/config/user_config.hpp | 193 +- src/graphics/callbacks.cpp | 795 ++ src/graphics/callbacks.hpp | 729 ++ src/graphics/camera.cpp | 33 +- src/graphics/camera.hpp | 4 +- src/graphics/explosion.cpp | 2 +- src/graphics/explosion.hpp | 2 +- src/graphics/glow.cpp | 85 + .../glow.hpp} | 43 +- src/graphics/glwrap.hpp | 21 + src/graphics/hardware_skinning.cpp | 2 +- src/graphics/hardware_skinning.hpp | 2 +- src/graphics/hit_effect.hpp | 2 +- src/graphics/hit_sfx.cpp | 2 +- src/graphics/hit_sfx.hpp | 2 +- src/graphics/irr_driver.cpp | 410 +- src/graphics/irr_driver.hpp | 202 +- src/graphics/large_mesh_buffer.hpp | 58 + src/graphics/lens_flare.cpp | 264 + src/graphics/lens_flare.hpp | 117 + src/graphics/light.cpp | 93 + src/graphics/light.hpp | 64 + src/graphics/lod_node.cpp | 22 +- src/graphics/lod_node.hpp | 10 +- src/graphics/material.cpp | 702 +- src/graphics/material.hpp | 32 +- src/graphics/material_manager.cpp | 7 +- src/graphics/material_manager.hpp | 3 +- src/graphics/mesh_tools.cpp | 2 +- src/graphics/mesh_tools.hpp | 2 +- src/graphics/mlaa_areamap.hpp | 200 + src/graphics/moving_texture.cpp | 2 +- src/graphics/moving_texture.hpp | 2 +- src/graphics/particle_emitter.cpp | 64 +- src/graphics/particle_emitter.hpp | 2 +- src/graphics/particle_kind.cpp | 12 +- src/graphics/particle_kind.hpp | 12 +- src/graphics/particle_kind_manager.cpp | 2 +- src/graphics/particle_kind_manager.hpp | 2 +- src/graphics/per_camera_node.cpp | 11 +- src/graphics/per_camera_node.hpp | 6 +- src/graphics/post_processing.cpp | 762 +- src/graphics/post_processing.hpp | 40 +- src/graphics/rain.cpp | 200 +- src/graphics/rain.hpp | 15 +- src/graphics/referee.cpp | 8 +- src/graphics/referee.hpp | 7 +- src/graphics/render.cpp | 836 +++ src/graphics/rtts.cpp | 154 + .../rtts.hpp} | 84 +- src/graphics/screenquad.cpp | 30 + src/graphics/screenquad.hpp | 91 + src/graphics/shaders.cpp | 264 + src/graphics/shaders.hpp | 118 + src/graphics/shadow.cpp | 3 +- src/graphics/shadow.hpp | 2 +- src/graphics/shadow_importance.cpp | 169 + src/graphics/shadow_importance.hpp | 41 + src/graphics/show_curve.cpp | 2 +- src/graphics/show_curve.hpp | 2 +- src/graphics/skid_marks.cpp | 38 +- src/graphics/skid_marks.hpp | 3 +- src/graphics/slip_stream.cpp | 2 +- src/graphics/slip_stream.hpp | 2 +- src/graphics/stars.cpp | 2 + src/graphics/stars.hpp | 2 + src/graphics/sun.cpp | 161 + .../sun.hpp} | 27 +- src/graphics/water.cpp | 94 + src/graphics/water.hpp | 72 + src/graphics/wind.cpp | 40 + .../wind.hpp} | 26 +- src/guiengine/CGUISpriteBank.cpp | 2 +- src/guiengine/abstract_state_manager.cpp | 2 +- src/guiengine/abstract_state_manager.hpp | 2 +- .../abstract_top_level_container.cpp | 2 +- .../abstract_top_level_container.hpp | 11 +- src/guiengine/dialog_queue.cpp | 82 + .../dialog_queue.hpp} | 44 +- src/guiengine/engine.cpp | 32 +- src/guiengine/engine.hpp | 4 +- src/guiengine/event_handler.cpp | 18 +- src/guiengine/event_handler.hpp | 2 +- src/guiengine/layout_manager.cpp | 11 +- src/guiengine/layout_manager.hpp | 2 +- src/guiengine/modaldialog.cpp | 20 +- src/guiengine/modaldialog.hpp | 15 +- src/guiengine/scalable_font.cpp | 28 +- src/guiengine/scalable_font.hpp | 8 +- src/guiengine/screen.cpp | 2 +- src/guiengine/screen.hpp | 7 +- src/guiengine/screen_loader.cpp | 2 +- src/guiengine/skin.cpp | 61 +- src/guiengine/skin.hpp | 2 +- src/guiengine/widget.cpp | 24 +- src/guiengine/widget.hpp | 13 +- src/guiengine/widgets.hpp | 17 + src/guiengine/widgets/CGUIEditBox.cpp | 1956 ++--- src/guiengine/widgets/CGUISTKListBox.cpp | 757 ++ src/guiengine/widgets/CGUISTKListBox.h | 198 + src/guiengine/widgets/bubble_widget.cpp | 2 +- src/guiengine/widgets/bubble_widget.hpp | 2 +- src/guiengine/widgets/button_widget.cpp | 2 +- src/guiengine/widgets/button_widget.hpp | 2 +- src/guiengine/widgets/check_box_widget.cpp | 2 +- src/guiengine/widgets/check_box_widget.hpp | 2 +- .../widgets/dynamic_ribbon_widget.cpp | 69 +- .../widgets/dynamic_ribbon_widget.hpp | 2 +- src/guiengine/widgets/icon_button_widget.cpp | 7 +- src/guiengine/widgets/icon_button_widget.hpp | 2 +- src/guiengine/widgets/label_widget.cpp | 45 +- src/guiengine/widgets/label_widget.hpp | 19 +- src/guiengine/widgets/list_widget.cpp | 175 +- src/guiengine/widgets/list_widget.hpp | 41 +- src/guiengine/widgets/model_view_widget.cpp | 2 +- src/guiengine/widgets/model_view_widget.hpp | 2 +- src/guiengine/widgets/progress_bar_widget.cpp | 2 +- src/guiengine/widgets/progress_bar_widget.hpp | 2 +- src/guiengine/widgets/rating_bar_widget.cpp | 103 +- src/guiengine/widgets/rating_bar_widget.hpp | 38 +- src/guiengine/widgets/ribbon_widget.cpp | 176 +- src/guiengine/widgets/ribbon_widget.hpp | 9 +- src/guiengine/widgets/spinner_widget.cpp | 28 +- src/guiengine/widgets/spinner_widget.hpp | 4 +- src/guiengine/widgets/text_box_widget.cpp | 11 +- src/guiengine/widgets/text_box_widget.hpp | 3 +- src/ide/Makefile.am | 23 - src/ide/Xcode/Config.xcconfig | 8 - .../Xcode/STK_XCode.xcodeproj/project.pbxproj | 3678 ---------- src/ide/Xcode/SuperTuxKart-Info.plist | 28 - src/ide/Xcode/cleanupRelease.sh | 4 - src/ide/Xcode/stk.icns | Bin 34633 -> 0 bytes src/ide/codeblocks/README | 35 - .../Readme - How to run stk on codeblocks.txt | 43 - src/ide/codeblocks/bullet_lib.cbp | 352 - src/ide/codeblocks/bullet_lib.depend | 1 - src/ide/codeblocks/bullet_lib.layout | 7 - src/ide/codeblocks/enet.cbp | 97 - src/ide/codeblocks/enet.depend | 85 - src/ide/codeblocks/enet.layout | 10 - src/ide/codeblocks/supertuxkart.cbp | 684 -- src/ide/codeblocks/supertuxkart.depend | 6472 ----------------- src/ide/codeblocks/supertuxkart.layout | 19 - src/ide/codeblocks/supertuxkart.workspace | 9 - src/ide/vc10/bullet_lib.vcxproj | 448 -- src/ide/vc10/enet.vcxproj | 160 - src/ide/vc10/supertuxkart.sln | 74 - src/ide/vc10/supertuxkart.vcxproj | 753 -- src/ide/vc10/supertuxkart.vcxproj.filters | 1701 ----- src/ide/vc11/bullet_lib.vcxproj | 452 -- src/ide/vc11/enet.vcxproj | 164 - src/ide/vc11/supertuxkart.sln | 190 - src/ide/vc11/supertuxkart.vcxproj | 737 -- src/ide/vc11/supertuxkart.vcxproj.filters | 1665 ----- src/ide/vc9/README | 35 - src/ide/vc9/bullet_lib.vcproj | 1662 ----- src/ide/vc9/enet.vcproj | 426 -- src/ide/vc9/supertuxkart.sln | 68 - src/ide/vc9/supertuxkart.vcproj | 2845 -------- src/input/binding.cpp | 2 +- src/input/binding.hpp | 2 +- src/input/device_manager.cpp | 2 +- src/input/device_manager.hpp | 4 +- src/input/input.hpp | 3 +- src/input/input_device.cpp | 17 + src/input/input_device.hpp | 18 + src/input/input_manager.cpp | 55 +- src/input/input_manager.hpp | 3 +- src/input/wiimote.cpp | 23 +- src/input/wiimote.hpp | 2 +- src/input/wiimote_manager.cpp | 49 +- src/input/wiimote_manager.hpp | 40 +- src/io/file_manager.cpp | 67 +- src/io/file_manager.hpp | 7 +- src/io/xml_node.cpp | 20 +- src/io/xml_node.hpp | 13 +- src/io/xml_writer.cpp | 2 +- src/io/xml_writer.hpp | 2 +- src/items/attachment.cpp | 17 +- src/items/attachment.hpp | 2 +- src/items/attachment_manager.cpp | 2 +- src/items/attachment_manager.hpp | 2 +- src/items/attachment_plugin.hpp | 2 +- src/items/bowling.cpp | 47 +- src/items/bowling.hpp | 3 +- src/items/cake.cpp | 26 +- src/items/cake.hpp | 6 +- src/items/flyable.cpp | 37 +- src/items/flyable.hpp | 7 +- src/items/item.cpp | 9 +- src/items/item.hpp | 3 +- src/items/item_manager.cpp | 15 +- src/items/item_manager.hpp | 2 +- src/items/plunger.cpp | 33 +- src/items/plunger.hpp | 5 +- src/items/powerup.cpp | 87 +- src/items/powerup.hpp | 2 +- src/items/powerup_manager.cpp | 2 +- src/items/powerup_manager.hpp | 2 +- src/items/projectile_manager.cpp | 56 +- src/items/projectile_manager.hpp | 3 +- src/items/rubber_ball.cpp | 32 +- src/items/rubber_ball.hpp | 4 +- src/items/rubber_band.cpp | 15 +- src/items/rubber_band.hpp | 2 +- src/items/swatter.cpp | 38 +- src/items/swatter.hpp | 4 +- src/karts/abstract_kart.cpp | 6 +- src/karts/abstract_kart.hpp | 37 +- src/karts/abstract_kart_animation.cpp | 2 +- src/karts/abstract_kart_animation.hpp | 6 +- src/karts/cannon_animation.cpp | 2 +- src/karts/cannon_animation.hpp | 2 +- src/karts/controller/ai_properties.cpp | 2 +- src/karts/controller/ai_properties.hpp | 2 +- src/karts/controller/battle_ai.cpp | 8 +- src/karts/controller/controller.cpp | 2 +- src/karts/controller/controller.hpp | 27 +- src/karts/controller/end_controller.cpp | 10 +- src/karts/controller/end_controller.hpp | 6 +- src/karts/controller/kart_control.hpp | 25 +- .../controller/network_player_controller.cpp | 368 + .../controller/network_player_controller.hpp | 48 + src/karts/controller/player_controller.cpp | 9 +- src/karts/controller/player_controller.hpp | 4 +- src/karts/controller/skidding_ai.cpp | 7 - src/karts/controller/skidding_ai.hpp | 6 +- src/karts/explosion_animation.cpp | 2 +- src/karts/explosion_animation.hpp | 2 +- src/karts/ghost_kart.cpp | 2 +- src/karts/ghost_kart.hpp | 2 +- src/karts/kart.cpp | 188 +- src/karts/kart.hpp | 11 +- src/karts/kart_gfx.cpp | 34 +- src/karts/kart_gfx.hpp | 2 +- src/karts/kart_model.cpp | 256 +- src/karts/kart_model.hpp | 81 +- src/karts/kart_properties.cpp | 19 +- src/karts/kart_properties.hpp | 23 +- src/karts/kart_properties_manager.cpp | 19 +- src/karts/kart_properties_manager.hpp | 6 +- src/karts/kart_with_stats.cpp | 2 +- src/karts/kart_with_stats.hpp | 2 +- src/karts/max_speed.cpp | 2 +- src/karts/max_speed.hpp | 2 +- src/karts/moveable.cpp | 5 +- src/karts/moveable.hpp | 7 +- src/karts/rescue_animation.cpp | 2 +- src/karts/rescue_animation.hpp | 2 +- src/karts/skidding.cpp | 7 +- src/karts/skidding.hpp | 2 +- src/karts/skidding_properties.cpp | 2 +- src/karts/skidding_properties.hpp | 2 +- src/main.cpp | 251 +- src/main_loop.cpp | 57 +- src/main_loop.hpp | 6 +- src/modes/cutscene_world.cpp | 6 +- src/modes/cutscene_world.hpp | 2 +- src/modes/demo_world.cpp | 7 +- src/modes/demo_world.hpp | 2 +- src/modes/easter_egg_hunt.cpp | 35 +- src/modes/easter_egg_hunt.hpp | 12 +- src/modes/follow_the_leader.cpp | 2 +- src/modes/follow_the_leader.hpp | 2 +- src/modes/linear_world.cpp | 11 +- src/modes/linear_world.hpp | 2 +- src/modes/overworld.cpp | 20 +- src/modes/overworld.hpp | 2 +- src/modes/profile_world.cpp | 2 +- src/modes/profile_world.hpp | 2 +- src/modes/soccer_world.cpp | 67 +- src/modes/soccer_world.hpp | 40 +- src/modes/standard_race.cpp | 6 +- src/modes/standard_race.hpp | 2 +- src/modes/three_strikes_battle.cpp | 46 +- src/modes/three_strikes_battle.hpp | 2 +- src/modes/tutorial_world.cpp | 18 + src/modes/tutorial_world.hpp | 18 + src/modes/world.cpp | 70 +- src/modes/world.hpp | 14 +- src/modes/world_status.cpp | 11 +- src/modes/world_status.hpp | 6 +- src/modes/world_with_rank.cpp | 2 +- src/modes/world_with_rank.hpp | 2 +- src/network/character_confirm_message.hpp | 71 - src/network/character_info_message.hpp | 50 - src/network/character_selected_message.hpp | 105 - src/network/client_network_manager.cpp | 166 + src/network/client_network_manager.hpp | 81 + src/network/connect_message.cpp | 77 - src/network/event.cpp | 96 + src/network/event.hpp | 87 + src/network/flyable_info.hpp | 70 - src/network/game_setup.cpp | 200 + src/network/game_setup.hpp | 104 + src/network/item_info.hpp | 66 - src/network/kart_control_message.cpp | 61 - src/network/kart_update_message.cpp | 62 - src/network/message.cpp | 225 - src/network/message.hpp | 143 - ...trol_message.hpp => network_interface.cpp} | 19 +- src/network/network_interface.hpp | 53 + src/network/network_kart.cpp | 43 - src/network/network_manager.cpp | 895 +-- src/network/network_manager.hpp | 187 +- src/network/network_string.cpp | 8 + src/network/network_string.hpp | 256 + src/network/network_world.cpp | 78 + src/network/network_world.hpp | 62 + src/network/protocol.cpp | 99 + src/network/protocol.hpp | 128 + src/network/protocol_manager.cpp | 527 ++ src/network/protocol_manager.hpp | 334 + .../protocols/client_lobby_room_protocol.cpp | 806 ++ .../protocols/client_lobby_room_protocol.hpp | 65 + src/network/protocols/connect_to_peer.cpp | 139 + src/network/protocols/connect_to_peer.hpp | 55 + src/network/protocols/connect_to_server.cpp | 283 + src/network/protocols/connect_to_server.hpp | 62 + .../protocols/controller_events_protocol.cpp | 171 + .../protocols/controller_events_protocol.hpp | 28 + .../protocols/game_events_protocol.cpp | 94 + .../protocols/game_events_protocol.hpp | 26 + src/network/protocols/get_peer_address.cpp | 97 + src/network/protocols/get_peer_address.hpp | 52 + src/network/protocols/get_public_address.cpp | 246 + src/network/protocols/get_public_address.hpp | 51 + src/network/protocols/hide_public_address.cpp | 82 + .../hide_public_address.hpp} | 50 +- .../protocols/kart_update_protocol.cpp | 137 + .../protocols/kart_update_protocol.hpp | 33 + .../lobby_room_protocol.cpp} | 24 +- src/network/protocols/lobby_room_protocol.hpp | 46 + src/network/protocols/ping_protocol.cpp | 48 + src/network/protocols/ping_protocol.hpp | 25 + src/network/protocols/quick_join_protocol.cpp | 88 + src/network/protocols/quick_join_protocol.hpp | 32 + src/network/protocols/request_connection.cpp | 93 + src/network/protocols/request_connection.hpp | 33 + .../protocols/server_lobby_room_protocol.cpp | 659 ++ .../protocols/server_lobby_room_protocol.hpp | 55 + src/network/protocols/show_public_address.cpp | 87 + src/network/protocols/show_public_address.hpp | 50 + src/network/protocols/start_game_protocol.cpp | 207 + src/network/protocols/start_game_protocol.hpp | 36 + src/network/protocols/start_server.cpp | 87 + src/network/protocols/start_server.hpp | 35 + src/network/protocols/stop_server.cpp | 90 + src/network/protocols/stop_server.hpp | 33 + .../protocols/synchronization_protocol.cpp | 183 + .../protocols/synchronization_protocol.hpp | 35 + src/network/race_config.cpp | 369 + src/network/race_config.hpp | 98 + src/network/race_info_message.cpp | 116 - src/network/race_result_ack_message.hpp | 58 - src/network/race_result_message.cpp | 60 - src/network/race_state.cpp | 195 - src/network/race_state.hpp | 109 - src/network/remote_kart_info.hpp | 14 +- src/network/server_network_manager.cpp | 132 + src/network/server_network_manager.hpp | 57 + src/network/singleton.hpp | 84 + src/network/stk_host.cpp | 369 + src/network/stk_host.hpp | 173 + src/network/stk_peer.cpp | 148 + src/network/stk_peer.hpp | 69 + src/network/types.cpp | 1 + src/network/types.hpp | 79 + src/online/current_user.cpp | 766 ++ src/online/current_user.hpp | 203 + src/online/http_manager.cpp | 300 + src/online/http_manager.hpp | 107 + src/online/messages.cpp | 110 + src/online/messages.hpp | 49 + src/online/profile.cpp | 281 + src/online/profile.hpp | 154 + src/online/profile_manager.cpp | 230 + src/online/profile_manager.hpp | 81 + src/online/request.cpp | 310 + src/online/request.hpp | 229 + src/online/server.cpp | 75 + src/online/server.hpp | 137 + src/online/servers_manager.cpp | 176 + src/online/servers_manager.hpp | 86 + src/physics/btKart.cpp | 81 +- src/physics/btKart.hpp | 40 +- src/physics/btKartRaycast.cpp | 34 +- src/physics/btKartRaycast.hpp | 14 +- src/physics/btUprightConstraint.cpp | 2 +- src/physics/btUprightConstraint.hpp | 4 +- src/physics/irr_debug_drawer.cpp | 2 +- src/physics/irr_debug_drawer.hpp | 2 +- src/physics/kart_motion_state.hpp | 9 +- src/physics/physical_object.cpp | 139 +- src/physics/physical_object.hpp | 57 +- src/physics/physics.cpp | 71 +- src/physics/physics.hpp | 2 +- src/physics/stk_dynamics_world.hpp | 4 +- src/physics/triangle_mesh.cpp | 2 +- src/physics/triangle_mesh.hpp | 2 +- src/physics/user_pointer.hpp | 2 +- src/race/grand_prix_data.cpp | 4 +- src/race/grand_prix_data.hpp | 4 +- src/race/grand_prix_manager.cpp | 2 +- src/race/grand_prix_manager.hpp | 2 +- src/race/highscore_manager.cpp | 4 +- src/race/highscore_manager.hpp | 2 +- src/race/highscores.cpp | 2 +- src/race/highscores.hpp | 2 +- src/race/history.cpp | 2 +- src/race/history.hpp | 3 +- src/race/race_manager.cpp | 89 +- src/race/race_manager.hpp | 31 +- src/replay/replay_base.cpp | 2 +- src/replay/replay_base.hpp | 2 +- src/replay/replay_play.cpp | 2 +- src/replay/replay_play.hpp | 2 +- src/replay/replay_recorder.cpp | 2 +- src/replay/replay_recorder.hpp | 2 +- src/states_screens/addons_screen.cpp | 57 +- src/states_screens/addons_screen.hpp | 2 +- src/states_screens/arenas_screen.cpp | 2 +- src/states_screens/arenas_screen.hpp | 2 +- src/states_screens/create_server_screen.cpp | 185 + src/states_screens/create_server_screen.hpp | 83 + src/states_screens/credits.cpp | 2 +- src/states_screens/credits.hpp | 2 +- src/states_screens/cutscene_gui.cpp | 2 +- src/states_screens/cutscene_gui.hpp | 2 +- .../dialogs/add_device_dialog.cpp | 6 +- .../dialogs/add_device_dialog.hpp | 2 +- src/states_screens/dialogs/addons_loading.cpp | 35 +- src/states_screens/dialogs/addons_loading.hpp | 16 +- .../dialogs/change_password_dialog.cpp | 184 + .../dialogs/change_password_dialog.hpp | 70 + .../dialogs/confirm_resolution_dialog.cpp | 2 +- .../dialogs/confirm_resolution_dialog.hpp | 2 +- .../dialogs/custom_video_settings.cpp | 47 +- .../dialogs/custom_video_settings.hpp | 2 +- .../dialogs/enter_player_name_dialog.cpp | 19 +- .../dialogs/enter_player_name_dialog.hpp | 2 +- src/states_screens/dialogs/gp_info_dialog.cpp | 49 +- src/states_screens/dialogs/gp_info_dialog.hpp | 2 +- src/states_screens/dialogs/login_dialog.cpp | 217 + src/states_screens/dialogs/login_dialog.hpp | 90 + src/states_screens/dialogs/message_dialog.cpp | 21 +- src/states_screens/dialogs/message_dialog.hpp | 11 +- .../dialogs/notification_dialog.cpp | 147 + .../dialogs/notification_dialog.hpp | 69 + .../dialogs/player_info_dialog.cpp | 2 +- .../dialogs/player_info_dialog.hpp | 2 +- .../dialogs/press_a_key_dialog.cpp | 2 +- .../dialogs/press_a_key_dialog.hpp | 2 +- .../dialogs/race_paused_dialog.cpp | 4 +- .../dialogs/race_paused_dialog.hpp | 2 +- .../dialogs/recovery_dialog.cpp | 190 + .../dialogs/recovery_dialog.hpp | 71 + .../dialogs/registration_dialog.cpp | 349 + .../dialogs/registration_dialog.hpp | 92 + .../dialogs/select_challenge.cpp | 6 +- .../dialogs/select_challenge.hpp | 2 +- .../dialogs/server_info_dialog.cpp | 175 + .../dialogs/server_info_dialog.hpp | 70 + .../dialogs/track_info_dialog.cpp | 6 +- .../dialogs/track_info_dialog.hpp | 2 +- .../dialogs/tutorial_message_dialog.cpp | 2 +- .../dialogs/tutorial_message_dialog.hpp | 2 +- .../dialogs/user_info_dialog.cpp | 229 + .../dialogs/user_info_dialog.hpp | 79 + src/states_screens/dialogs/vote_dialog.cpp | 176 + src/states_screens/dialogs/vote_dialog.hpp | 66 + src/states_screens/easter_egg_screen.cpp | 301 + src/states_screens/easter_egg_screen.hpp | 61 + src/states_screens/feature_unlocked.cpp | 17 +- src/states_screens/feature_unlocked.hpp | 4 +- src/states_screens/grand_prix_lose.cpp | 17 +- src/states_screens/grand_prix_lose.hpp | 4 +- src/states_screens/grand_prix_win.cpp | 26 +- src/states_screens/grand_prix_win.hpp | 6 +- src/states_screens/help_screen_1.cpp | 25 +- src/states_screens/help_screen_1.hpp | 2 +- src/states_screens/help_screen_2.cpp | 2 +- src/states_screens/help_screen_2.hpp | 2 +- src/states_screens/help_screen_3.cpp | 2 +- src/states_screens/help_screen_3.hpp | 2 +- src/states_screens/help_screen_4.cpp | 2 +- src/states_screens/help_screen_4.hpp | 2 +- src/states_screens/kart_selection.cpp | 1554 ++-- src/states_screens/kart_selection.hpp | 241 +- src/states_screens/main_menu_screen.cpp | 23 +- src/states_screens/main_menu_screen.hpp | 2 +- src/states_screens/network_kart_selection.cpp | 184 + src/states_screens/network_kart_selection.hpp | 29 + src/states_screens/networking_lobby.cpp | 152 + src/states_screens/networking_lobby.hpp | 85 + src/states_screens/offline_kart_selection.cpp | 15 + src/states_screens/offline_kart_selection.hpp | 20 + .../online_profile_achievements.cpp | 134 + .../online_profile_achievements.hpp | 67 + src/states_screens/online_profile_base.cpp | 109 + src/states_screens/online_profile_base.hpp | 63 + src/states_screens/online_profile_friends.cpp | 142 + src/states_screens/online_profile_friends.hpp | 69 + .../online_profile_overview.cpp | 67 + .../online_profile_overview.hpp | 55 + .../online_profile_settings.cpp | 73 + .../online_profile_settings.hpp | 56 + src/states_screens/online_screen.cpp | 298 + src/states_screens/online_screen.hpp | 92 + src/states_screens/online_user_search.cpp | 212 + src/states_screens/online_user_search.hpp | 85 + src/states_screens/options_screen_audio.cpp | 2 +- src/states_screens/options_screen_audio.hpp | 2 +- src/states_screens/options_screen_input.cpp | 2 +- src/states_screens/options_screen_input.hpp | 2 +- src/states_screens/options_screen_input2.cpp | 2 +- src/states_screens/options_screen_input2.hpp | 2 +- src/states_screens/options_screen_players.cpp | 2 +- src/states_screens/options_screen_players.hpp | 2 +- src/states_screens/options_screen_ui.cpp | 20 +- src/states_screens/options_screen_ui.hpp | 2 +- src/states_screens/options_screen_video.cpp | 87 +- src/states_screens/options_screen_video.hpp | 2 +- src/states_screens/race_gui.cpp | 32 +- src/states_screens/race_gui.hpp | 6 +- src/states_screens/race_gui_base.cpp | 51 +- src/states_screens/race_gui_base.hpp | 6 +- src/states_screens/race_gui_overworld.cpp | 4 +- src/states_screens/race_gui_overworld.hpp | 4 +- src/states_screens/race_result_gui.cpp | 51 +- src/states_screens/race_result_gui.hpp | 2 +- src/states_screens/race_setup_screen.cpp | 17 +- src/states_screens/race_setup_screen.hpp | 2 +- src/states_screens/server_selection.cpp | 218 + src/states_screens/server_selection.hpp | 86 + src/states_screens/soccer_setup_screen.cpp | 30 +- src/states_screens/soccer_setup_screen.hpp | 2 +- src/states_screens/state_manager.cpp | 18 +- src/states_screens/state_manager.hpp | 43 +- src/states_screens/story_mode_lobby.cpp | 2 +- src/states_screens/story_mode_lobby.hpp | 2 +- src/states_screens/tracks_screen.cpp | 20 +- src/states_screens/tracks_screen.hpp | 2 +- src/tinygettext/dictionary.cpp | 2 +- src/tinygettext/dictionary.hpp | 2 +- src/tinygettext/dictionary_manager.cpp | 2 +- src/tinygettext/dictionary_manager.hpp | 2 +- src/tinygettext/file_system.hpp | 2 +- src/tinygettext/iconv.cpp | 2 +- src/tinygettext/iconv.hpp | 2 +- src/tinygettext/language.cpp | 2 +- src/tinygettext/language.hpp | 2 +- src/tinygettext/log_stream.hpp | 2 +- src/tinygettext/plural_forms.cpp | 2 +- src/tinygettext/plural_forms.hpp | 2 +- src/tinygettext/po_parser.cpp | 68 +- src/tinygettext/po_parser.hpp | 2 +- src/tinygettext/stk_file_system.cpp | 2 +- src/tinygettext/stk_file_system.hpp | 2 +- src/tinygettext/tgt_log.cpp | 2 +- src/tinygettext/tgt_log.hpp | 2 +- src/tinygettext/tinygettext.cpp | 2 +- src/tinygettext/tinygettext.hpp | 2 +- src/tracks/ambient_light_sphere.cpp | 2 +- src/tracks/ambient_light_sphere.hpp | 2 +- src/tracks/bezier_curve.cpp | 2 +- src/tracks/bezier_curve.hpp | 2 +- src/tracks/check_cannon.cpp | 2 +- src/tracks/check_cannon.hpp | 4 +- src/tracks/check_goal.cpp | 2 +- src/tracks/check_goal.hpp | 2 +- src/tracks/check_lap.cpp | 2 +- src/tracks/check_lap.hpp | 2 +- src/tracks/check_line.cpp | 2 +- src/tracks/check_line.hpp | 2 +- src/tracks/check_manager.cpp | 2 +- src/tracks/check_manager.hpp | 2 +- src/tracks/check_sphere.cpp | 2 +- src/tracks/check_sphere.hpp | 2 +- src/tracks/check_structure.cpp | 6 +- src/tracks/check_structure.hpp | 4 +- src/tracks/graph_node.cpp | 2 +- src/tracks/graph_node.hpp | 2 +- src/tracks/lod_node_loader.cpp | 2 +- src/tracks/lod_node_loader.hpp | 2 +- src/tracks/quad.cpp | 2 +- src/tracks/quad.hpp | 2 +- src/tracks/quad_graph.cpp | 30 +- src/tracks/quad_graph.hpp | 2 +- src/tracks/quad_set.cpp | 2 +- src/tracks/quad_set.hpp | 2 +- src/tracks/terrain_info.cpp | 2 +- src/tracks/terrain_info.hpp | 2 +- src/tracks/track.cpp | 103 +- src/tracks/track.hpp | 52 +- src/tracks/track_manager.cpp | 2 +- src/tracks/track_manager.hpp | 2 +- src/tracks/track_object.cpp | 40 +- src/tracks/track_object.hpp | 4 +- src/tracks/track_object_manager.cpp | 2 +- src/tracks/track_object_manager.hpp | 2 +- src/tracks/track_object_presentation.cpp | 31 +- src/tracks/track_object_presentation.hpp | 24 +- src/tracks/track_sector.cpp | 2 +- src/tracks/track_sector.hpp | 2 +- src/utils/aligned_array.hpp | 2 +- src/utils/constants.cpp | 3 +- src/utils/constants.hpp | 5 +- src/utils/crash_reporting.cpp | 330 + src/utils/crash_reporting.hpp | 31 + src/utils/helpers.cpp | 220 + .../connect_message.hpp => utils/helpers.hpp} | 35 +- src/utils/interpolation_array.hpp | 2 +- src/utils/leak_check.cpp | 2 +- src/utils/leak_check.hpp | 2 +- src/utils/log.cpp | 6 +- src/utils/log.hpp | 2 +- src/utils/no_copy.hpp | 33 +- src/utils/profiler.cpp | 18 +- src/utils/profiler.hpp | 8 +- src/utils/ptr_vector.hpp | 31 +- src/utils/random_generator.cpp | 2 +- src/utils/random_generator.hpp | 3 +- src/utils/string_utils.cpp | 131 +- src/utils/string_utils.hpp | 14 +- src/utils/synchronised.hpp | 29 +- src/utils/time.cpp | 21 +- src/utils/time.hpp | 31 +- src/utils/translation.cpp | 2 +- src/utils/translation.hpp | 2 +- src/utils/types.hpp | 35 + src/utils/vec3.cpp | 2 +- src/utils/vec3.hpp | 2 +- src/utils/vs.hpp | 12 + src/windows_installer/logo_slim.bmp | Bin 0 -> 25818 bytes src/windows_installer/stk_installer.bmp | Bin 0 -> 531830 bytes src/windows_installer/supertuxkart.nsi | 23 +- 700 files changed, 32314 insertions(+), 30745 deletions(-) create mode 100644 src/achievements/achievement.cpp create mode 100644 src/achievements/achievement.hpp create mode 100644 src/achievements/achievement_info.cpp create mode 100644 src/achievements/achievement_info.hpp create mode 100644 src/achievements/achievements_manager.cpp create mode 100644 src/achievements/achievements_manager.hpp create mode 100644 src/achievements/achievements_slot.cpp create mode 100644 src/achievements/achievements_slot.hpp create mode 100644 src/graphics/callbacks.cpp create mode 100644 src/graphics/callbacks.hpp create mode 100644 src/graphics/glow.cpp rename src/{network/race_start_message.hpp => graphics/glow.hpp} (55%) create mode 100644 src/graphics/glwrap.hpp create mode 100644 src/graphics/large_mesh_buffer.hpp create mode 100644 src/graphics/lens_flare.cpp create mode 100644 src/graphics/lens_flare.hpp create mode 100644 src/graphics/light.cpp create mode 100644 src/graphics/light.hpp create mode 100644 src/graphics/mlaa_areamap.hpp create mode 100644 src/graphics/render.cpp create mode 100644 src/graphics/rtts.cpp rename src/{network/race_result_message.hpp => graphics/rtts.hpp} (51%) create mode 100644 src/graphics/screenquad.cpp create mode 100644 src/graphics/screenquad.hpp create mode 100644 src/graphics/shaders.cpp create mode 100644 src/graphics/shaders.hpp create mode 100644 src/graphics/shadow_importance.cpp create mode 100644 src/graphics/shadow_importance.hpp create mode 100644 src/graphics/sun.cpp rename src/{network/race_info_message.hpp => graphics/sun.hpp} (68%) create mode 100644 src/graphics/water.cpp create mode 100644 src/graphics/water.hpp create mode 100644 src/graphics/wind.cpp rename src/{network/kart_update_message.hpp => graphics/wind.hpp} (73%) create mode 100644 src/guiengine/dialog_queue.cpp rename src/{network/network_kart.hpp => guiengine/dialog_queue.hpp} (54%) create mode 100644 src/guiengine/widgets/CGUISTKListBox.cpp create mode 100644 src/guiengine/widgets/CGUISTKListBox.h delete mode 100644 src/ide/Makefile.am delete mode 100644 src/ide/Xcode/Config.xcconfig delete mode 100644 src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj delete mode 100644 src/ide/Xcode/SuperTuxKart-Info.plist delete mode 100755 src/ide/Xcode/cleanupRelease.sh delete mode 100644 src/ide/Xcode/stk.icns delete mode 100644 src/ide/codeblocks/README delete mode 100755 src/ide/codeblocks/Readme - How to run stk on codeblocks.txt delete mode 100644 src/ide/codeblocks/bullet_lib.cbp delete mode 100644 src/ide/codeblocks/bullet_lib.depend delete mode 100644 src/ide/codeblocks/bullet_lib.layout delete mode 100644 src/ide/codeblocks/enet.cbp delete mode 100644 src/ide/codeblocks/enet.depend delete mode 100644 src/ide/codeblocks/enet.layout delete mode 100644 src/ide/codeblocks/supertuxkart.cbp delete mode 100644 src/ide/codeblocks/supertuxkart.depend delete mode 100644 src/ide/codeblocks/supertuxkart.layout delete mode 100644 src/ide/codeblocks/supertuxkart.workspace delete mode 100644 src/ide/vc10/bullet_lib.vcxproj delete mode 100644 src/ide/vc10/enet.vcxproj delete mode 100644 src/ide/vc10/supertuxkart.sln delete mode 100644 src/ide/vc10/supertuxkart.vcxproj delete mode 100644 src/ide/vc10/supertuxkart.vcxproj.filters delete mode 100644 src/ide/vc11/bullet_lib.vcxproj delete mode 100644 src/ide/vc11/enet.vcxproj delete mode 100644 src/ide/vc11/supertuxkart.sln delete mode 100644 src/ide/vc11/supertuxkart.vcxproj delete mode 100644 src/ide/vc11/supertuxkart.vcxproj.filters delete mode 100644 src/ide/vc9/README delete mode 100644 src/ide/vc9/bullet_lib.vcproj delete mode 100644 src/ide/vc9/enet.vcproj delete mode 100644 src/ide/vc9/supertuxkart.sln delete mode 100644 src/ide/vc9/supertuxkart.vcproj create mode 100644 src/karts/controller/network_player_controller.cpp create mode 100644 src/karts/controller/network_player_controller.hpp delete mode 100644 src/network/character_confirm_message.hpp delete mode 100644 src/network/character_info_message.hpp delete mode 100644 src/network/character_selected_message.hpp create mode 100644 src/network/client_network_manager.cpp create mode 100644 src/network/client_network_manager.hpp delete mode 100644 src/network/connect_message.cpp create mode 100644 src/network/event.cpp create mode 100644 src/network/event.hpp delete mode 100644 src/network/flyable_info.hpp create mode 100644 src/network/game_setup.cpp create mode 100644 src/network/game_setup.hpp delete mode 100644 src/network/item_info.hpp delete mode 100644 src/network/kart_control_message.cpp delete mode 100644 src/network/kart_update_message.cpp delete mode 100644 src/network/message.cpp delete mode 100644 src/network/message.hpp rename src/network/{kart_control_message.hpp => network_interface.cpp} (68%) create mode 100644 src/network/network_interface.hpp delete mode 100644 src/network/network_kart.cpp create mode 100644 src/network/network_string.cpp create mode 100644 src/network/network_string.hpp create mode 100644 src/network/network_world.cpp create mode 100644 src/network/network_world.hpp create mode 100644 src/network/protocol.cpp create mode 100644 src/network/protocol.hpp create mode 100644 src/network/protocol_manager.cpp create mode 100644 src/network/protocol_manager.hpp create mode 100644 src/network/protocols/client_lobby_room_protocol.cpp create mode 100644 src/network/protocols/client_lobby_room_protocol.hpp create mode 100644 src/network/protocols/connect_to_peer.cpp create mode 100644 src/network/protocols/connect_to_peer.hpp create mode 100644 src/network/protocols/connect_to_server.cpp create mode 100644 src/network/protocols/connect_to_server.hpp create mode 100644 src/network/protocols/controller_events_protocol.cpp create mode 100644 src/network/protocols/controller_events_protocol.hpp create mode 100644 src/network/protocols/game_events_protocol.cpp create mode 100644 src/network/protocols/game_events_protocol.hpp create mode 100644 src/network/protocols/get_peer_address.cpp create mode 100644 src/network/protocols/get_peer_address.hpp create mode 100644 src/network/protocols/get_public_address.cpp create mode 100644 src/network/protocols/get_public_address.hpp create mode 100644 src/network/protocols/hide_public_address.cpp rename src/network/{num_players_message.hpp => protocols/hide_public_address.hpp} (50%) create mode 100644 src/network/protocols/kart_update_protocol.cpp create mode 100644 src/network/protocols/kart_update_protocol.hpp rename src/network/{world_loaded_message.hpp => protocols/lobby_room_protocol.cpp} (65%) create mode 100644 src/network/protocols/lobby_room_protocol.hpp create mode 100644 src/network/protocols/ping_protocol.cpp create mode 100644 src/network/protocols/ping_protocol.hpp create mode 100644 src/network/protocols/quick_join_protocol.cpp create mode 100644 src/network/protocols/quick_join_protocol.hpp create mode 100644 src/network/protocols/request_connection.cpp create mode 100644 src/network/protocols/request_connection.hpp create mode 100644 src/network/protocols/server_lobby_room_protocol.cpp create mode 100644 src/network/protocols/server_lobby_room_protocol.hpp create mode 100644 src/network/protocols/show_public_address.cpp create mode 100644 src/network/protocols/show_public_address.hpp create mode 100644 src/network/protocols/start_game_protocol.cpp create mode 100644 src/network/protocols/start_game_protocol.hpp create mode 100644 src/network/protocols/start_server.cpp create mode 100644 src/network/protocols/start_server.hpp create mode 100644 src/network/protocols/stop_server.cpp create mode 100644 src/network/protocols/stop_server.hpp create mode 100644 src/network/protocols/synchronization_protocol.cpp create mode 100644 src/network/protocols/synchronization_protocol.hpp create mode 100644 src/network/race_config.cpp create mode 100644 src/network/race_config.hpp delete mode 100644 src/network/race_info_message.cpp delete mode 100644 src/network/race_result_ack_message.hpp delete mode 100644 src/network/race_result_message.cpp delete mode 100644 src/network/race_state.cpp delete mode 100644 src/network/race_state.hpp create mode 100644 src/network/server_network_manager.cpp create mode 100644 src/network/server_network_manager.hpp create mode 100644 src/network/singleton.hpp create mode 100644 src/network/stk_host.cpp create mode 100644 src/network/stk_host.hpp create mode 100644 src/network/stk_peer.cpp create mode 100644 src/network/stk_peer.hpp create mode 100644 src/network/types.cpp create mode 100644 src/network/types.hpp create mode 100644 src/online/current_user.cpp create mode 100644 src/online/current_user.hpp create mode 100644 src/online/http_manager.cpp create mode 100644 src/online/http_manager.hpp create mode 100644 src/online/messages.cpp create mode 100644 src/online/messages.hpp create mode 100644 src/online/profile.cpp create mode 100644 src/online/profile.hpp create mode 100644 src/online/profile_manager.cpp create mode 100644 src/online/profile_manager.hpp create mode 100644 src/online/request.cpp create mode 100644 src/online/request.hpp create mode 100644 src/online/server.cpp create mode 100644 src/online/server.hpp create mode 100644 src/online/servers_manager.cpp create mode 100644 src/online/servers_manager.hpp create mode 100644 src/states_screens/create_server_screen.cpp create mode 100644 src/states_screens/create_server_screen.hpp create mode 100644 src/states_screens/dialogs/change_password_dialog.cpp create mode 100644 src/states_screens/dialogs/change_password_dialog.hpp create mode 100644 src/states_screens/dialogs/login_dialog.cpp create mode 100644 src/states_screens/dialogs/login_dialog.hpp create mode 100644 src/states_screens/dialogs/notification_dialog.cpp create mode 100644 src/states_screens/dialogs/notification_dialog.hpp create mode 100644 src/states_screens/dialogs/recovery_dialog.cpp create mode 100644 src/states_screens/dialogs/recovery_dialog.hpp create mode 100644 src/states_screens/dialogs/registration_dialog.cpp create mode 100644 src/states_screens/dialogs/registration_dialog.hpp create mode 100644 src/states_screens/dialogs/server_info_dialog.cpp create mode 100644 src/states_screens/dialogs/server_info_dialog.hpp create mode 100644 src/states_screens/dialogs/user_info_dialog.cpp create mode 100644 src/states_screens/dialogs/user_info_dialog.hpp create mode 100644 src/states_screens/dialogs/vote_dialog.cpp create mode 100644 src/states_screens/dialogs/vote_dialog.hpp create mode 100644 src/states_screens/easter_egg_screen.cpp create mode 100644 src/states_screens/easter_egg_screen.hpp create mode 100644 src/states_screens/network_kart_selection.cpp create mode 100644 src/states_screens/network_kart_selection.hpp create mode 100644 src/states_screens/networking_lobby.cpp create mode 100644 src/states_screens/networking_lobby.hpp create mode 100644 src/states_screens/offline_kart_selection.cpp create mode 100644 src/states_screens/offline_kart_selection.hpp create mode 100644 src/states_screens/online_profile_achievements.cpp create mode 100644 src/states_screens/online_profile_achievements.hpp create mode 100644 src/states_screens/online_profile_base.cpp create mode 100644 src/states_screens/online_profile_base.hpp create mode 100644 src/states_screens/online_profile_friends.cpp create mode 100644 src/states_screens/online_profile_friends.hpp create mode 100644 src/states_screens/online_profile_overview.cpp create mode 100644 src/states_screens/online_profile_overview.hpp create mode 100644 src/states_screens/online_profile_settings.cpp create mode 100644 src/states_screens/online_profile_settings.hpp create mode 100644 src/states_screens/online_screen.cpp create mode 100644 src/states_screens/online_screen.hpp create mode 100644 src/states_screens/online_user_search.cpp create mode 100644 src/states_screens/online_user_search.hpp create mode 100644 src/states_screens/server_selection.cpp create mode 100644 src/states_screens/server_selection.hpp create mode 100644 src/utils/crash_reporting.cpp create mode 100644 src/utils/crash_reporting.hpp create mode 100644 src/utils/helpers.cpp rename src/{network/connect_message.hpp => utils/helpers.hpp} (63%) create mode 100644 src/utils/types.hpp create mode 100644 src/utils/vs.hpp create mode 100644 src/windows_installer/logo_slim.bmp create mode 100644 src/windows_installer/stk_installer.bmp diff --git a/src/Makefile.am b/src/Makefile.am index 16177fb07..40d714555 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -69,10 +69,14 @@ supertuxkart_SOURCES = \ config/device_config.hpp \ graphics/CBatchingMesh.cpp \ graphics/CBatchingMesh.hpp \ + graphics/callbacks.cpp \ + graphics/callbacks.hpp \ graphics/camera.cpp \ graphics/camera.hpp \ graphics/explosion.cpp \ graphics/explosion.hpp \ + graphics/glow.cpp \ + graphics/glow.hpp \ graphics/hardware_skinning.cpp \ graphics/hardware_skinning.hpp \ graphics/hit_effect.hpp \ @@ -80,6 +84,11 @@ supertuxkart_SOURCES = \ graphics/hit_sfx.hpp \ graphics/irr_driver.cpp \ graphics/irr_driver.hpp \ + graphics/large_mesh_buffer.hpp \ + graphics/lens_flare.cpp \ + graphics/lens_flare.hpp \ + graphics/light.hpp \ + graphics/light.cpp \ graphics/lod_node.cpp \ graphics/lod_node.hpp \ graphics/material.cpp \ @@ -102,10 +111,19 @@ supertuxkart_SOURCES = \ graphics/post_processing.hpp \ graphics/rain.cpp \ graphics/rain.hpp \ + graphics/render.cpp \ graphics/referee.cpp \ graphics/referee.hpp \ + graphics/rtts.hpp \ + graphics/rtts.cpp \ + graphics/screenquad.cpp \ + graphics/screenquad.hpp \ + graphics/shaders.cpp \ + graphics/shaders.hpp \ graphics/shadow.cpp \ graphics/shadow.hpp \ + graphics/shadow_importance.cpp \ + graphics/shadow_importance.hpp \ graphics/show_curve.cpp \ graphics/show_curve.hpp \ graphics/skid_marks.cpp \ @@ -114,6 +132,12 @@ supertuxkart_SOURCES = \ graphics/slip_stream.hpp \ graphics/stars.cpp \ graphics/stars.hpp \ + graphics/sun.hpp \ + graphics/sun.cpp \ + graphics/water.hpp \ + graphics/water.cpp \ + graphics/wind.hpp \ + graphics/wind.cpp \ guiengine/CGUISpriteBank.cpp \ guiengine/CGUISpriteBank.h \ guiengine/abstract_state_manager.cpp \ @@ -496,6 +520,8 @@ supertuxkart_SOURCES = \ utils/aligned_array.hpp \ utils/constants.hpp \ utils/constants.cpp \ + utils/helpers.hpp \ + utils/helpers.cpp \ utils/leak_check.cpp \ utils/leak_check.hpp \ utils/log.cpp \ @@ -518,7 +544,8 @@ supertuxkart_SOURCES = \ utils/utf8/core.h \ utils/utf8/unchecked.h \ utils/vec3.cpp \ - utils/vec3.hpp + utils/vec3.hpp \ + utils/vs.hpp # Link in the specific gcc 4.1 bug work around supertuxkart_LDADD = \ diff --git a/src/achievements/achievement.cpp b/src/achievements/achievement.cpp new file mode 100644 index 000000000..0c16eafdf --- /dev/null +++ b/src/achievements/achievement.cpp @@ -0,0 +1,209 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "achievements/achievement.hpp" + +#include "achievements/achievement_info.hpp" +#include "guiengine/dialog_queue.hpp" +#include "states_screens/dialogs/notification_dialog.hpp" +#include "io/xml_writer.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" + + +#include +#include +#include + +// ============================================================================ +Achievement::Achievement(const AchievementInfo * info) + :m_achievement_info(info) +{ + m_id = info->getID(); + m_achieved = false; +} + +// ============================================================================ +Achievement::~Achievement() +{ + +} + +// ============================================================================ +void Achievement::onRaceEnd() +{ + if(m_achievement_info->needsResetAfterRace()) + this->reset(); +} + +// ============================================================================ +void Achievement::check() +{ + if(m_achieved) + return; + + if(m_achievement_info->checkCompletion(this)) + { + //show achievement + GUIEngine::DialogQueue::get()->pushDialog( + new NotificationDialog(NotificationDialog::T_Achievements, + irr::core::stringw(_("Completed achievement")) + irr::core::stringw(" \"") + m_achievement_info->getTitle() + irr::core::stringw("\".") + )); + //send to server + Online::CurrentUser::get()->onAchieving(m_id); + m_achieved = true; + } +} + +// ============================================================================ +SingleAchievement::SingleAchievement(const AchievementInfo * info) + : Achievement(info) +{ + m_progress = 0; + m_achieved = false; +} + +// ============================================================================ +void SingleAchievement::load(XMLNode * input) +{ + std::string achieved(""); + input->get("achieved", &achieved); + if(achieved == "true") + { + m_achieved = true; + return; + } + input->get("value", &m_progress); +} +// ============================================================================ +void SingleAchievement::save(std::ofstream & out) +{ + out << " \n"; +} // save + +// ============================================================================ +void SingleAchievement::reset() +{ + m_progress = 0; +} // reset + +// ============================================================================ +void SingleAchievement::increase(int increase) +{ + m_progress += increase; + check(); +} + +// ============================================================================ +irr::core::stringw SingleAchievement::getProgressAsString() +{ + return StringUtils::toWString(m_progress) + "/" + StringUtils::toWString(((SingleAchievementInfo *) m_achievement_info)->getGoalValue()); +} + +// ============================================================================ +MapAchievement::MapAchievement(const AchievementInfo * info) + : Achievement(info) +{ +} + +// ============================================================================ +void MapAchievement::load(XMLNode * input) +{ + std::string achieved(""); + input->get("achieved", &achieved); + if(achieved == "true") + { + m_achieved = true; + return; + } + std::vector xml_entries; + input->getNodes("entry", xml_entries); + for (unsigned int n=0; n < xml_entries.size(); n++) + { + std::string key(""); + xml_entries[n]->get("key", &key); + int value(0); + xml_entries[n]->get("value", &value); + m_progress_map[key] = value; + } +} + +// ============================================================================ +void MapAchievement::save(std::ofstream & out) +{ + out << " \n"; + if(!m_achieved) + { + std::map::iterator iter; + for ( iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter ) { + out << " first.c_str() << "\" value=\"" << StringUtils::toString(iter->second) << "\"/>\n"; + } + } + out << " \n"; +} // save + +// ============================================================================ + +int MapAchievement::getValue(const std::string & key) +{ + if ( m_progress_map.find(key) != m_progress_map.end()) + return m_progress_map[key]; + return 0; +} + + +// ============================================================================ +void MapAchievement::reset() +{ + std::map::iterator iter; + for ( iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter ) { + iter->second = 0; + } +} // reset + +// ============================================================================ +void MapAchievement::increase(const std::string & key, int increase) +{ + if ( m_progress_map.find(key) != m_progress_map.end()) + { + m_progress_map[key] += increase; + check(); + } +} + +// ============================================================================ +irr::core::stringw MapAchievement::getProgressAsString() +{ + int progress(0); + int goal(0); + const std::map goal_values = ((MapAchievementInfo *) m_achievement_info)->getGoalValues(); + std::map::const_iterator iter; + for ( iter = goal_values.begin(); iter != goal_values.end(); ++iter ) { + goal += iter->second; + progress += m_progress_map[iter->first]; + } + return StringUtils::toWString(progress) + "/" + StringUtils::toWString(goal); +} + diff --git a/src/achievements/achievement.hpp b/src/achievements/achievement.hpp new file mode 100644 index 000000000..8da035877 --- /dev/null +++ b/src/achievements/achievement.hpp @@ -0,0 +1,101 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ACHIEVEMENT_HPP +#define HEADER_ACHIEVEMENT_HPP + +#include "utils/types.hpp" + +#include +#include +#include "io/xml_node.hpp" + + +// ============================================================================ + +/** + * \brief + * \ingroup + */ +class AchievementInfo; + +class Achievement +{ +protected: + uint32_t m_id; + bool m_achieved; + const AchievementInfo * m_achievement_info; + void check (); + +public: + Achievement (const AchievementInfo * info); + virtual ~Achievement (); + uint32_t getID () const { return m_id; } + const AchievementInfo * getInfo () const { return m_achievement_info;} + virtual void load (XMLNode * input) = 0; + virtual void save (std::ofstream & out) = 0; + virtual void reset () = 0; + void onRaceEnd (); + void setAchieved () {m_achieved = true; }; + virtual irr::core::stringw getProgressAsString () = 0; + + enum AchievementType + { + AT_SINGLE, + AT_MAP + }; + +}; // class Achievement + +class SingleAchievement : public Achievement +{ +protected: + int m_progress; + +public: + SingleAchievement (const AchievementInfo * info); + virtual ~SingleAchievement () {}; + + void load (XMLNode * input); + int getValue () const { return m_progress; } + void save (std::ofstream & out); + void increase (int increase = 1); + void reset (); + virtual irr::core::stringw getProgressAsString (); +}; // class SingleAchievement + +class MapAchievement : public Achievement +{ +protected: + std::map m_progress_map; + +public: + MapAchievement (const AchievementInfo * info); + virtual ~MapAchievement () {}; + + void load (XMLNode * input); + int getValue (const std::string & key); + void increase (const std::string & key, int increase = 1); + void save (std::ofstream & out); + void reset (); + virtual irr::core::stringw getProgressAsString (); +}; // class MapAchievement + +#endif + +/*EOF*/ diff --git a/src/achievements/achievement_info.cpp b/src/achievements/achievement_info.cpp new file mode 100644 index 000000000..eda7b4c73 --- /dev/null +++ b/src/achievements/achievement_info.cpp @@ -0,0 +1,87 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "achievements/achievement_info.hpp" + +#include "utils/log.hpp" +#include "utils/translation.hpp" +#include "io/xml_writer.hpp" + + +#include +#include +#include + +// ============================================================================ +AchievementInfo::AchievementInfo(const XMLNode * input) +{ + input->get("id", &m_id); + input->get("title", &m_title); + input->get("description", &m_description); + + std::string reset_after_race(""); + input->get("reset_after_race", &reset_after_race); + m_reset_after_race = reset_after_race == "true"; + +} + +// ============================================================================ +SingleAchievementInfo::SingleAchievementInfo(const XMLNode * input) + : AchievementInfo(input) +{ + input->get("goal", &m_goal_value); +} + +// ============================================================================ +bool SingleAchievementInfo::checkCompletion(Achievement * achievement) const +{ + SingleAchievement * single_achievement = (SingleAchievement *) achievement; + if(single_achievement->getValue() >= m_goal_value) + return true; + return false; +} + +// ============================================================================ +MapAchievementInfo::MapAchievementInfo(const XMLNode * input) + : AchievementInfo(input) +{ + std::vector xml_entries; + input->getNodes("entry", xml_entries); + for (unsigned int n=0; n < xml_entries.size(); n++) + { + std::string key(""); + xml_entries[n]->get("key", &key); + int goal(0); + xml_entries[n]->get("goal", &goal); + m_goal_values[key] = goal; + } + if(m_goal_values.size() != xml_entries.size()) + Log::error("MapAchievementInfo","Duplicate keys for the entries of a MapAchievement found."); +} + +// ============================================================================ +bool MapAchievementInfo::checkCompletion(Achievement * achievement) const +{ + MapAchievement * map_achievement = (MapAchievement *) achievement; + std::map::const_iterator iter; + for ( iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++ ) { + if(map_achievement->getValue(iter->first) < iter->second) + return false; + } + return true; +} diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp new file mode 100644 index 000000000..626e1d132 --- /dev/null +++ b/src/achievements/achievement_info.hpp @@ -0,0 +1,87 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ACHIEVEMENT_INFO_HPP +#define HEADER_ACHIEVEMENT_INFO_HPP + +#include "utils/types.hpp" + +#include +#include +#include "io/xml_node.hpp" +#include "achievements/achievement.hpp" + + +// ============================================================================ + +class Achievement; + +/** + * \brief + * \ingroup + */ +class AchievementInfo +{ +protected: + uint32_t m_id; + irr::core::stringw m_title; + irr::core::stringw m_description; + bool m_reset_after_race; + +public: + AchievementInfo (const XMLNode * input); + virtual ~AchievementInfo () {}; + uint32_t getID () const { return m_id; } + irr::core::stringw getDescription () const { return m_description; } + irr::core::stringw getTitle () const { return m_title; } + virtual Achievement::AchievementType getType () const = 0; + virtual bool checkCompletion (Achievement * achievement) const = 0; + bool needsResetAfterRace() const {return m_reset_after_race; } +}; // class AchievementInfo + +class SingleAchievementInfo : public AchievementInfo +{ +protected: + int m_goal_value; + +public: + SingleAchievementInfo (const XMLNode * input); + virtual ~SingleAchievementInfo () {}; + int getGoalValue () const { return m_goal_value; } + virtual bool checkCompletion (Achievement * achievement) const; + + virtual Achievement::AchievementType getType() const { return Achievement::AT_SINGLE; }; +}; // class SingleAchievementInfo + +class MapAchievementInfo : public AchievementInfo +{ +protected: + std::map m_goal_values; + +public: + MapAchievementInfo (const XMLNode * input); + virtual ~MapAchievementInfo () {}; + int getGoalValue (const std::string & key) { return m_goal_values[key];} + const std::map & getGoalValues() const {return m_goal_values;} + virtual bool checkCompletion (Achievement * achievement) const; + virtual Achievement::AchievementType getType() const { return Achievement::AT_MAP; }; +}; // class MapAchievementInfo + +#endif + +/*EOF*/ diff --git a/src/achievements/achievements_manager.cpp b/src/achievements/achievements_manager.cpp new file mode 100644 index 000000000..cc81f3ee6 --- /dev/null +++ b/src/achievements/achievements_manager.cpp @@ -0,0 +1,247 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "achievements/achievements_manager.hpp" + +#include "utils/log.hpp" +#include "utils/translation.hpp" +#include "io/file_manager.hpp" +#include "io/xml_writer.hpp" +#include "config/player.hpp" +#include "config/user_config.hpp" +#include "online/current_user.hpp" +#include "challenges/unlock_manager.hpp" + +#include +#include +#include + +static AchievementsManager* achievements_manager_singleton(NULL); + +AchievementsManager* AchievementsManager::get() +{ + if (achievements_manager_singleton == NULL) + achievements_manager_singleton = new AchievementsManager(); + return achievements_manager_singleton; +} + +void AchievementsManager::deallocate() +{ + delete achievements_manager_singleton; + achievements_manager_singleton = NULL; +} // deallocate + +// ============================================================================ +AchievementsManager::AchievementsManager() +{ + parseDataFile(); +} + + +// ============================================================================ +void AchievementsManager::init() +{ + parseConfigFile(); +} + +// ============================================================================ +AchievementsManager::~AchievementsManager() +{ + save(); + m_slots.clearAndDeleteAll(); + std::map::iterator it; + for ( it = m_achievements_info.begin(); it != m_achievements_info.end(); ++it ) { + delete it->second; + } + m_achievements_info.clear(); +} + +// ============================================================================ +void AchievementsManager::parseDataFile() +{ + const std::string file_name = file_manager->getDataFile("achievements.xml"); + const XMLNode *root = file_manager->createXMLTree(file_name); + unsigned int num_nodes = root->getNumNodes(); + for(unsigned int i = 0; i < num_nodes; i++) + { + const XMLNode *node = root->getNode(i); + std::string type(""); + node->get("type", &type); + AchievementInfo * achievement_info; + if(type == "single") + { + achievement_info = new SingleAchievementInfo(node); + } + else if(type == "map") + { + achievement_info = new MapAchievementInfo(node); + } + else + { + Log::error("AchievementsManager::parseAchievements","Non-existent achievement type. Skipping - definitely results in unwanted behaviour."); + continue; + } + m_achievements_info[achievement_info->getID()] = achievement_info; + } + if(num_nodes != m_achievements_info.size()) + Log::error("AchievementsManager::parseAchievements","Multiple achievements with the same id!"); +} + + +// ============================================================================ +void AchievementsManager::parseConfigFile() +{ + const std::string filename=file_manager->getConfigFile("achievements.xml"); + XMLNode* root = file_manager->createXMLTree(filename); + if(!root || root->getName() != "achievements") + { + Log::info("AchievementsManager", "Achievements file '%s' will be created.",filename.c_str()); + createSlotsIfNeeded(); + if (root) delete root; + return; + } + + std::vector xml_slots; + root->getNodes("slot", xml_slots); + for (unsigned int n=0; n < xml_slots.size(); n++) + { + AchievementsSlot * slot = new AchievementsSlot(xml_slots[n]); + if(!slot->isValid()) + { + Log::warn("AchievementsManager", "Found game slot with faulty or missing information. Discarding it."); + delete slot; + continue; + } + m_slots.push_back(slot); + } + delete root; +} // load + + +AchievementsSlot * AchievementsManager::createNewSlot(std::string id, bool online) +{ + AchievementsSlot* slot = new AchievementsSlot(id, online); + m_slots.push_back(slot); + return slot; +} + + +//----------------------------------------------------------------------------- +/** Creates a slot for players that don't have one yet + * \return true if any were created + */ +void AchievementsManager::createSlotsIfNeeded() +{ + bool something_changed = false; + + // make sure all players have at least one game slot associated + PtrVector& players = UserConfigParams::m_all_players; + for (int n=0; ngetConfigFile("achievements.xml"); + + std::ofstream achievements_file(filename.c_str(), std::ios::out); + + if (!achievements_file.is_open()) + { + Log::warn("AchievementsManager::save", + "Failed to open '%s' for writing, achievements won't be saved\n", + filename.c_str()); + return; + } + + achievements_file << "\n"; + achievements_file << "\n"; + + for (int i = 0; i < m_slots.size(); i++) + { + m_slots[i].save(achievements_file); + } + + achievements_file << "\n\n"; + achievements_file.close(); +} + + +// ============================================================================ +void AchievementsManager::onRaceEnd() +{ + //reset all values that need to be reset + m_active_slot->onRaceEnd(); +} + +// ============================================================================ +AchievementsSlot * AchievementsManager::getSlot(const std::string & id, bool online) +{ + for(int i = 0; i < m_slots.size(); i++) + { + if(m_slots[i].isOnline() == online && m_slots[i].getID() == id) + { + return m_slots.get(i); + } + } + return NULL; +} + +// ============================================================================ +void AchievementsManager::updateCurrentPlayer() +{ + if(Online::CurrentUser::get()->isRegisteredUser()) + { + m_active_slot = getSlot(StringUtils::toString(Online::CurrentUser::get()->getID()), true); + if(m_active_slot == NULL) + { + m_active_slot = createNewSlot(StringUtils::toString(Online::CurrentUser::get()->getID()), true); + save(); + } + } + else + { + m_active_slot = getSlot(unlock_manager->getCurrentPlayer()->getUniqueID(), false); + if(m_active_slot == NULL) + { + m_active_slot = createNewSlot(unlock_manager->getCurrentPlayer()->getUniqueID(), false); + save(); + } + } +} + + +// ============================================================================ +AchievementInfo * AchievementsManager::getAchievementInfo(uint32_t id) +{ + if ( m_achievements_info.find(id) != m_achievements_info.end()) + return m_achievements_info[id]; + return NULL; +} diff --git a/src/achievements/achievements_manager.hpp b/src/achievements/achievements_manager.hpp new file mode 100644 index 000000000..2800d4641 --- /dev/null +++ b/src/achievements/achievements_manager.hpp @@ -0,0 +1,70 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ACHIEVEMENTS_MANAGER_HPP +#define HEADER_ACHIEVEMENTS_MANAGER_HPP + +#include "utils/types.hpp" +#include "utils/ptr_vector.hpp" +#include "achievements/achievement_info.hpp" +#include "achievements/achievements_slot.hpp" + + +#include +#include +#include +#include + + +// ============================================================================ + +/** + * \brief Class that takes care of online profiles + * \ingroup online + */ +class AchievementsManager +{ +private : + AchievementsSlot * m_active_slot; + PtrVector m_slots; + std::map m_achievements_info; + AchievementsManager (); + ~AchievementsManager (); + AchievementsSlot * createNewSlot(std::string id, bool online); + void parseDataFile(); + void parseConfigFile(); + +public: + /**Singleton */ + static AchievementsManager * get(); + static void deallocate(); + + void init(); + void save(); + void onRaceEnd(); + void updateCurrentPlayer(); + AchievementsSlot * getActive() const { return m_active_slot; } + AchievementsSlot * getSlot(const std::string & id, bool online); + void createSlotsIfNeeded(); + AchievementInfo * getAchievementInfo(uint32_t id); + const std::map & getAllInfo() { return m_achievements_info;} +}; // class AchievementsManager + +#endif + +/*EOF*/ diff --git a/src/achievements/achievements_slot.cpp b/src/achievements/achievements_slot.cpp new file mode 100644 index 000000000..9acbe78c5 --- /dev/null +++ b/src/achievements/achievements_slot.cpp @@ -0,0 +1,153 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "achievements/achievements_slot.hpp" + +#include "achievements/achievement_info.hpp" +#include "achievements/achievements_manager.hpp" +#include "utils/log.hpp" +#include "utils/ptr_vector.hpp" +#include "utils/translation.hpp" +#include "io/xml_writer.hpp" +#include "online/current_user.hpp" + +#include +#include +#include +// ============================================================================ +AchievementsSlot::AchievementsSlot(const XMLNode * input) +{ + int fetched_user_id = input->get("user_id", &m_id); + std::string online; + int fetched_online = input->get("online", &online); + if(!fetched_user_id || !fetched_online || !(online == "true" || online == "false")) + { + m_valid = false; + } + m_valid = true; + m_online = online == "true"; + + createFreshSlot(); + + std::vector xml_achievements; + input->getNodes("achievement", xml_achievements); + for( unsigned int i=0; iget("id", &achievement_id); + Achievement * achievement = getAchievement(achievement_id); + if(achievement == NULL) + { + Log::warn("AchievementsSlot", "Found saved achievement data for a non-existent achievement. Discarding."); + continue; + } + achievement->load(xml_achievements[i]); + } +} + +// ============================================================================ +AchievementsSlot::AchievementsSlot(std::string id, bool online) +{ + m_valid = true; + m_online = online; + m_id = id; + + createFreshSlot(); +} + +// ============================================================================ +AchievementsSlot::~AchievementsSlot() +{ + deleteAchievements(); +} + +// ============================================================================ +void AchievementsSlot::deleteAchievements() +{ + std::map::iterator it; + for ( it = m_achievements.begin(); it != m_achievements.end(); ++it ) { + delete it->second; + } + m_achievements.clear(); +} + +// ============================================================================ +void AchievementsSlot::createFreshSlot() +{ + deleteAchievements(); + const std::map all_info = AchievementsManager::get()->getAllInfo(); + std::map::const_iterator it; + for ( it = all_info.begin(); it != all_info.end(); ++it ) { + Achievement::AchievementType achievement_type = it->second->getType(); + Achievement * achievement; + if(achievement_type == Achievement::AT_SINGLE) + { + achievement = new SingleAchievement(it->second); + } + else if(achievement_type == Achievement::AT_MAP) + { + achievement = new MapAchievement(it->second); + } + m_achievements[achievement->getID()] = achievement; + } +} + +// ============================================================================ +void AchievementsSlot::save(std::ofstream & out) +{ + out << " \n"; + std::map::const_iterator i; + for(i = m_achievements.begin(); i != m_achievements.end(); i++) + { + if (i->second != NULL) + i->second->save(out); + } + out << " \n"; +} + +// ============================================================================ +Achievement * AchievementsSlot::getAchievement(uint32_t id) +{ + if ( m_achievements.find(id) != m_achievements.end()) + return m_achievements[id]; + return NULL; +} + +// ============================================================================ +void AchievementsSlot::sync(const std::vector & achieved_ids) +{ + for(unsigned int i =0; i < achieved_ids.size(); ++i) + { + Achievement * achievement = getAchievement(achieved_ids[i]); + if(achievement != NULL) + achievement->setAchieved(); + } +} + +// ============================================================================ +void AchievementsSlot::onRaceEnd() +{ + //reset all values that need to be reset + std::map::iterator iter; + for ( iter = m_achievements.begin(); iter != m_achievements.end(); ++iter ) { + iter->second->onRaceEnd(); + } +} diff --git a/src/achievements/achievements_slot.hpp b/src/achievements/achievements_slot.hpp new file mode 100644 index 000000000..97ed65d72 --- /dev/null +++ b/src/achievements/achievements_slot.hpp @@ -0,0 +1,64 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ACHIEVEMENTS_SLOT_HPP +#define HEADER_ACHIEVEMENTS_SLOT_HPP + +#include "utils/types.hpp" +#include "achievements/achievement.hpp" +#include "online/http_manager.hpp" + +#include +#include +#include "io/xml_node.hpp" + + +class AchievementsSlot +{ +private: + std::map m_achievements; + bool m_online; + bool m_valid; + std::string m_id; + + void createFreshSlot(); + void deleteAchievements(); + + class SyncAchievementsRequest : public Online::XMLRequest { + virtual void callback (); + public: + SyncAchievementsRequest() : Online::XMLRequest(true) {} + }; + +public : + AchievementsSlot(const XMLNode * input); + AchievementsSlot(std::string id, bool online); + ~AchievementsSlot(); + bool isValid() const { return m_valid;} + void save(std::ofstream & out); + bool isOnline() const {return m_online;} + void sync(const std::vector & achieved_ids); + void onRaceEnd(); + const std::string & getID() const {return m_id;} + const std::map & getAllAchievements() {return m_achievements;} + Achievement * getAchievement(uint32_t id); +}; + +#endif + +/*EOF*/ diff --git a/src/addons/addon.cpp b/src/addons/addon.cpp index 515b9e03d..924facdd1 100644 --- a/src/addons/addon.cpp +++ b/src/addons/addon.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin, Joerg Henrichs +// Copyright (C) 2010-2013 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/addon.hpp b/src/addons/addon.hpp index 9eaca0639..fc3c111f1 100644 --- a/src/addons/addon.hpp +++ b/src/addons/addon.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin, Joerg Henrichs +// Copyright (C) 2010-2013 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -115,7 +115,7 @@ private: /** Compressed size of the addon package. */ int m_size; /** Rating for thsi addon package. */ - float m_rating; + mutable float m_rating; /** Minimum version addon is included with. */ std::string m_min_include_ver; /** Maximum version addon is included with. */ @@ -151,6 +151,9 @@ public: /** Returns the rating of an addon. */ const float getRating() const {return m_rating; } // ------------------------------------------------------------------------ + /** Sets the rating of an addon. */ + void setRating(const float rating) const {m_rating = rating; } + // ------------------------------------------------------------------------ /** Returns the type of the addon. */ const std::string& getType() const { return m_type; } // ------------------------------------------------------------------------ diff --git a/src/addons/addons_manager.cpp b/src/addons/addons_manager.cpp index b8920f4a5..4d50a63c4 100644 --- a/src/addons/addons_manager.cpp +++ b/src/addons/addons_manager.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin +// Copyright (C) 2010-2013 Lucas Baudin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -456,22 +456,22 @@ bool AddonsManager::uninstall(const Addon &addon) m_addons_list.getData()[index].setInstalled(false); //remove the addons directory - bool error = false; - // if the user deleted the data directory for an add-on with - // filesystem tools, removeTrack/removeKart will trigger an assert - // because the kart/track was never added in the first place - if (file_manager->fileExists(addon.getDataDir())) - { - error = !file_manager->removeDirectory(addon.getDataDir()); - if(addon.getType()=="kart") - { - kart_properties_manager->removeKart(addon.getId()); - } - else if(addon.getType()=="track" || addon.getType()=="arena") - { - track_manager->removeTrack(addon.getId()); - } - } + bool error = false; + // if the user deleted the data directory for an add-on with + // filesystem tools, removeTrack/removeKart will trigger an assert + // because the kart/track was never added in the first place + if (file_manager->fileExists(addon.getDataDir())) + { + error = !file_manager->removeDirectory(addon.getDataDir()); + if(addon.getType()=="kart") + { + kart_properties_manager->removeKart(addon.getId()); + } + else if(addon.getType()=="track" || addon.getType()=="arena") + { + track_manager->removeTrack(addon.getId()); + } + } saveInstalled(); return !error; } // uninstall diff --git a/src/addons/addons_manager.hpp b/src/addons/addons_manager.hpp index d3b0545d8..e5d90bdf8 100644 --- a/src/addons/addons_manager.hpp +++ b/src/addons/addons_manager.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin, Joerg Henrichs +// Copyright (C) 2010-2013 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/dummy_network_http.hpp b/src/addons/dummy_network_http.hpp index cf58b8859..7b00fe586 100644 --- a/src/addons/dummy_network_http.hpp +++ b/src/addons/dummy_network_http.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin +// Copyright (C) 2010-2013 Lucas Baudin // 2011 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or diff --git a/src/addons/inetwork_http.cpp b/src/addons/inetwork_http.cpp index 6dc8f64c6..d1a8b9e96 100644 --- a/src/addons/inetwork_http.cpp +++ b/src/addons/inetwork_http.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/inetwork_http.hpp b/src/addons/inetwork_http.hpp index ed55aa215..7a49bf723 100644 --- a/src/addons/inetwork_http.hpp +++ b/src/addons/inetwork_http.hpp @@ -1,6 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin -// 2011 Lucas Baudin, Joerg Henrichs +// Copyright (C) 2010-2013 Lucas Baudin +// Copyright (C) 2011-2013 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/network_http.cpp b/src/addons/network_http.cpp index da8587a4b..977197d21 100644 --- a/src/addons/network_http.cpp +++ b/src/addons/network_http.cpp @@ -1,6 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin -// 2011 Lucas Baudin, Joerg Henrichs +// Copyright (C) 2010-2013 Lucas Baudin +// Copyright (C) 2011-2013 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,14 +24,7 @@ #include #include #include - -#if defined(WIN32) && !defined(__CYGWIN__) -# include -# define isnan _isnan -#else -# include -# include -#endif +#include #include "addons/news_manager.hpp" #include "addons/request.hpp" @@ -494,19 +487,19 @@ CURLcode NetworkHttp::downloadFileInternal(Request *request) curl_easy_setopt(m_curl_session, CURLOPT_URL, full_url.c_str()); std::string uagent = (std::string)"SuperTuxKart/" + STK_VERSION; - // Add platform to user-agent string for informational purposes. - // Add more cases as necessary. - #ifdef WIN32 - uagent += (std::string)" (Windows)"; - #elif defined(__APPLE__) - uagent += (std::string)" (Macintosh)"; - #elif defined(__FreeBSD__) - uagent += (std::string)" (FreeBSD)"; - #elif defined(linux) - uagent += (std::string)" (Linux)"; - #else - // Unknown system type - #endif + // Add platform to user-agent string for informational purposes. + // Add more cases as necessary. + #ifdef WIN32 + uagent += (std::string)" (Windows)"; + #elif defined(__APPLE__) + uagent += (std::string)" (Macintosh)"; + #elif defined(__FreeBSD__) + uagent += (std::string)" (FreeBSD)"; + #elif defined(linux) + uagent += (std::string)" (Linux)"; + #else + // Unknown system type + #endif curl_easy_setopt(m_curl_session, CURLOPT_USERAGENT, uagent.c_str()); curl_easy_setopt(m_curl_session, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(m_curl_session, CURLOPT_PROGRESSDATA, request); diff --git a/src/addons/network_http.hpp b/src/addons/network_http.hpp index 923b90cb2..5ea3d0f0d 100644 --- a/src/addons/network_http.hpp +++ b/src/addons/network_http.hpp @@ -1,6 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin -// 2011 Lucas Baudin, Joerg Henrichs +// Copyright (C) 2010-2013 Lucas Baudin +// Copyright (C) 2011-2013 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,7 +27,7 @@ #include #ifdef WIN32 -# include +# include #endif #include diff --git a/src/addons/news_manager.cpp b/src/addons/news_manager.cpp index 5f70e4f92..eadf6d7b1 100644 --- a/src/addons/news_manager.cpp +++ b/src/addons/news_manager.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/news_manager.hpp b/src/addons/news_manager.hpp index ba1770e7b..f55baa138 100644 --- a/src/addons/news_manager.hpp +++ b/src/addons/news_manager.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/request.cpp b/src/addons/request.cpp index 3a8d7595a..645d44e14 100644 --- a/src/addons/request.cpp +++ b/src/addons/request.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/request.hpp b/src/addons/request.hpp index 4b6f540f9..9090d84ee 100644 --- a/src/addons/request.hpp +++ b/src/addons/request.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/zip.cpp b/src/addons/zip.cpp index 0d6001faa..6cadcb7f9 100644 --- a/src/addons/zip.cpp +++ b/src/addons/zip.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin +// Copyright (C) 2010-2013 Lucas Baudin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/addons/zip.hpp b/src/addons/zip.hpp index e49faab2c..e78f515cc 100644 --- a/src/addons/zip.hpp +++ b/src/addons/zip.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin +// Copyright (C) 2010-2013 Lucas Baudin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/animations/animation_base.cpp b/src/animations/animation_base.cpp index e6aec59df..cd51bf3c1 100644 --- a/src/animations/animation_base.cpp +++ b/src/animations/animation_base.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,14 +21,10 @@ #include "animations/ipo.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" +#include "utils/vs.hpp" #include - -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include AnimationBase::AnimationBase(const XMLNode &node) diff --git a/src/animations/animation_base.hpp b/src/animations/animation_base.hpp index 53aa55fab..ae84c6447 100644 --- a/src/animations/animation_base.hpp +++ b/src/animations/animation_base.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 96063cf52..d5c4db071 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,15 +19,11 @@ #include "animations/ipo.hpp" #include "io/xml_node.hpp" +#include "utils/vs.hpp" #include #include - -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include const std::string Ipo::m_all_channel_names[IPO_MAX] = {"LocX", "LocY", "LocZ", "LocXYZ", diff --git a/src/animations/ipo.hpp b/src/animations/ipo.hpp index 308ae87a5..a0a93eff4 100644 --- a/src/animations/ipo.hpp +++ b/src/animations/ipo.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/animations/three_d_animation.cpp b/src/animations/three_d_animation.cpp index a920602bc..5a64db219 100644 --- a/src/animations/three_d_animation.cpp +++ b/src/animations/three_d_animation.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/animations/three_d_animation.hpp b/src/animations/three_d_animation.hpp index 1acd0cbd1..451ed2a82 100644 --- a/src/animations/three_d_animation.hpp +++ b/src/animations/three_d_animation.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/dummy_sfx.hpp b/src/audio/dummy_sfx.hpp index 812828ada..9840c2233 100644 --- a/src/audio/dummy_sfx.hpp +++ b/src/audio/dummy_sfx.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann -// Copyright (C) 2008 Joerg Henrichs, Patrick Ammann +// Copyright (C) 2006-2013 Patrick Ammann +// Copyright (C) 2008-2013 Joerg Henrichs, Patrick Ammann // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music.hpp b/src/audio/music.hpp index 0ed725960..824f4d3de 100644 --- a/src/audio/music.hpp +++ b/src/audio/music.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann +// Copyright (C) 2006-2013 Patrick Ammann // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music_dummy.hpp b/src/audio/music_dummy.hpp index 16efd4e96..977c4a5b7 100644 --- a/src/audio/music_dummy.hpp +++ b/src/audio/music_dummy.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann +// Copyright (C) 2006-2013 Patrick Ammann // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music_information.cpp b/src/audio/music_information.cpp index dd489bffe..2d7cc8e86 100644 --- a/src/audio/music_information.cpp +++ b/src/audio/music_information.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music_information.hpp b/src/audio/music_information.hpp index 4af4a3f21..07d1aebfa 100644 --- a/src/audio/music_information.hpp +++ b/src/audio/music_information.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 590d74161..9999c2a46 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann -// Copyright (C) 2008 Patrick Ammann , Joerg Henrichs +// Copyright (C) 2006-2013 Patrick Ammann +// Copyright (C) 2008-2013 Patrick Ammann , Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music_manager.hpp b/src/audio/music_manager.hpp index ef6887f96..cee900a25 100644 --- a/src/audio/music_manager.hpp +++ b/src/audio/music_manager.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann -// Copyright (C) 2008 Patrick Ammann , Joerg Henrichs +// Copyright (C) 2006-2013 Patrick Ammann +// Copyright (C) 2008-2013 Patrick Ammann , Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music_ogg.cpp b/src/audio/music_ogg.cpp index c009dc717..711769df9 100644 --- a/src/audio/music_ogg.cpp +++ b/src/audio/music_ogg.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Damien Morel +// Copyright (C) 2007-2013 Damien Morel // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/music_ogg.hpp b/src/audio/music_ogg.hpp index afd1f899e..2478ee454 100644 --- a/src/audio/music_ogg.hpp +++ b/src/audio/music_ogg.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Damien Morel +// Copyright (C) 2007-2013 Damien Morel // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/sfx_base.hpp b/src/audio/sfx_base.hpp index b06daa7ae..39229fd3f 100644 --- a/src/audio/sfx_base.hpp +++ b/src/audio/sfx_base.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann -// Copyright (C) 2008 Joerg Henrichs, Patrick Ammann +// Copyright (C) 2006-2013 Patrick Ammann +// Copyright (C) 2008-2013 Joerg Henrichs, Patrick Ammann // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/sfx_buffer.cpp b/src/audio/sfx_buffer.cpp index 422c696be..8bcefbfec 100644 --- a/src/audio/sfx_buffer.cpp +++ b/src/audio/sfx_buffer.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -18,6 +18,7 @@ #include "audio/sfx_buffer.hpp" #include "audio/sfx_manager.hpp" +#include "config/user_config.hpp" #include "io/file_manager.hpp" #include "utils/constants.hpp" @@ -76,6 +77,8 @@ SFXBuffer::SFXBuffer(const std::string& file, bool SFXBuffer::load() { + if (UserConfigParams::m_sfx == false) return false; + #if HAVE_OGGVORBIS if (m_loaded) return false; diff --git a/src/audio/sfx_buffer.hpp b/src/audio/sfx_buffer.hpp index 4efbf53aa..98ffbc8e4 100644 --- a/src/audio/sfx_buffer.hpp +++ b/src/audio/sfx_buffer.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index cc5d21969..6b282209f 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -307,7 +307,7 @@ SFXBase* SFXManager::createSoundSource(SFXBuffer* buffer, // race_manager->getNumLocalPlayers(), buffer->isPositional()); #if HAVE_OGGVORBIS - assert( alIsBuffer(buffer->getBufferID()) ); + //assert( alIsBuffer(buffer->getBufferID()) ); crashes on server SFXBase* sfx = new SFXOpenAL(buffer, positional, buffer->getGain(), owns_buffer); #else SFXBase* sfx = new DummySFX(buffer, positional, buffer->getGain(), owns_buffer); diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index f99dbcc32..2342a3a23 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 3ccb8b30d..a08d9b63b 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann -// 2009-2011 Marianne Gagnon +// Copyright (C) 2006-2013 Patrick Ammann +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,6 +25,7 @@ #include "config/user_config.hpp" #include "io/file_manager.hpp" #include "race/race_manager.hpp" +#include "utils/vs.hpp" #ifdef __APPLE__ # include @@ -33,16 +34,10 @@ #endif #include +#include #include #include -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif - - SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float gain, bool ownsBuffer) : SFXBase() { m_soundBuffer = buffer; diff --git a/src/audio/sfx_openal.hpp b/src/audio/sfx_openal.hpp index 9921f002a..5ba26e8e0 100644 --- a/src/audio/sfx_openal.hpp +++ b/src/audio/sfx_openal.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Patrick Ammann +// Copyright (C) 2006-2013 Patrick Ammann // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/challenges/challenge.cpp b/src/challenges/challenge.cpp index 31fe02036..29d9e2af8 100644 --- a/src/challenges/challenge.cpp +++ b/src/challenges/challenge.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -90,17 +90,11 @@ void Challenge::setSolved(RaceManager::Difficulty d) //----------------------------------------------------------------------------- - -const char* boolstr(bool b) -{ - return (b ? "true" : "false"); -} - void Challenge::save(std::ofstream& writer) { writer << " <" << m_data->getId().c_str() << ">\n" - << " \n" - << " \n" - << " \n" + << " \n" + << " \n" + << " \n" << " getId().c_str() << ">\n"; } // save diff --git a/src/challenges/challenge.hpp b/src/challenges/challenge.hpp index 739cc4e5b..7d18c8f49 100644 --- a/src/challenges/challenge.hpp +++ b/src/challenges/challenge.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/challenges/challenge_data.cpp b/src/challenges/challenge_data.cpp index a18c08ea5..b0fe57a68 100644 --- a/src/challenges/challenge_data.cpp +++ b/src/challenges/challenge_data.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/challenges/challenge_data.hpp b/src/challenges/challenge_data.hpp index 7912a06f1..ba3bff33e 100644 --- a/src/challenges/challenge_data.hpp +++ b/src/challenges/challenge_data.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/challenges/game_slot.cpp b/src/challenges/game_slot.cpp index e571db816..2ac0449ac 100644 --- a/src/challenges/game_slot.cpp +++ b/src/challenges/game_slot.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/challenges/game_slot.hpp b/src/challenges/game_slot.hpp index 1994b0bc9..11dedf407 100644 --- a/src/challenges/game_slot.hpp +++ b/src/challenges/game_slot.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/challenges/unlock_manager.cpp b/src/challenges/unlock_manager.cpp index 6d9d730e5..538d113c3 100644 --- a/src/challenges/unlock_manager.cpp +++ b/src/challenges/unlock_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,6 +24,7 @@ #include #include +#include "achievements/achievements_manager.hpp" #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" #include "config/player.hpp" @@ -204,7 +205,7 @@ const ChallengeData* UnlockManager::getChallenge(const std::string& id) */ void UnlockManager::load() { - const std::string filename=file_manager->getChallengeFile("challenges.xml"); + const std::string filename=file_manager->getConfigFile("challenges.xml"); XMLNode* root = file_manager->createXMLTree(filename); if(!root || root->getName() != "challenges") { @@ -263,7 +264,7 @@ void UnlockManager::load() void UnlockManager::save() { - std::string filename = file_manager->getChallengeFile("challenges.xml"); + std::string filename = file_manager->getConfigFile("challenges.xml"); std::ofstream challenge_file(filename.c_str(), std::ios::out); @@ -421,6 +422,15 @@ void UnlockManager::updateActiveChallengeList() getCurrentSlot()->computeActive(); } + +//----------------------------------------------------------------------------- +void UnlockManager::setCurrentSlot(std::string slotid) +{ + m_current_game_slot = slotid; + AchievementsManager::get()->updateCurrentPlayer(); +} + + //----------------------------------------------------------------------------- void UnlockManager::findWhatWasUnlocked(int points_before, int points_now, diff --git a/src/challenges/unlock_manager.hpp b/src/challenges/unlock_manager.hpp index 7a56ade22..093efcc29 100644 --- a/src/challenges/unlock_manager.hpp +++ b/src/challenges/unlock_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -82,8 +82,7 @@ public: } /** \param slotid name of the player */ - void setCurrentSlot(std::string slotid) { m_current_game_slot = slotid; } - + void setCurrentSlot(std::string slotid); void findWhatWasUnlocked(int pointsBefore, int pointsNow, std::vector& tracks, std::vector& gps); diff --git a/src/config/device_config.cpp b/src/config/device_config.cpp index dfaec0eeb..32db91569 100644 --- a/src/config/device_config.cpp +++ b/src/config/device_config.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/config/device_config.hpp b/src/config/device_config.hpp index 8c148a460..775d8506f 100644 --- a/src/config/device_config.hpp +++ b/src/config/device_config.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/config/player.cpp b/src/config/player.cpp index 542d56972..179c30775 100644 --- a/src/config/player.cpp +++ b/src/config/player.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 +// Copyright (C) 2012-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/config/player.hpp b/src/config/player.hpp index c0c68a779..809d5416e 100644 --- a/src/config/player.hpp +++ b/src/config/player.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/config/saved_grand_prix.cpp b/src/config/saved_grand_prix.cpp index 21d581b0c..bf8f2b01d 100644 --- a/src/config/saved_grand_prix.cpp +++ b/src/config/saved_grand_prix.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 +// Copyright (C) 2013-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/config/saved_grand_prix.hpp b/src/config/saved_grand_prix.hpp index 89b061be5..a0789a59e 100644 --- a/src/config/saved_grand_prix.hpp +++ b/src/config/saved_grand_prix.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/config/stk_config.cpp b/src/config/stk_config.cpp index b3f840e74..638f23da8 100644 --- a/src/config/stk_config.cpp +++ b/src/config/stk_config.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -181,6 +181,19 @@ void STKConfig::init_defaults() m_disable_steer_while_unskid = false; m_camera_follow_skid = false; + m_nitro_glow_color[0] = 1.0f; + m_nitro_glow_color[1] = 1.0f; + m_nitro_glow_color[2] = 1.0f; + m_box_glow_color[0] = 1.0f; + m_box_glow_color[1] = 1.0f; + m_box_glow_color[2] = 1.0f; + m_banana_glow_color[0] = 1.0f; + m_banana_glow_color[1] = 1.0f; + m_banana_glow_color[2] = 1.0f; + m_bubblegum_glow_color[0] = 1.0f; + m_bubblegum_glow_color[1] = 1.0f; + m_bubblegum_glow_color[2] = 1.0f; + m_score_increase.clear(); m_leader_intervals.clear(); m_switch_items.clear(); @@ -362,6 +375,39 @@ void STKConfig::getAllData(const XMLNode * root) replay_node->get("delta-pos", &m_replay_delta_pos2 ); replay_node->get("delta-t", &m_replay_dt ); } + + if(const XMLNode *colors = root->getNode("glow-colors")) + { + video::SColor tmpcol; + if (colors->get("nitro", &tmpcol)) + { + m_nitro_glow_color[0] = tmpcol.getRed() / 255.0f; + m_nitro_glow_color[1] = tmpcol.getGreen() / 255.0f; + m_nitro_glow_color[2] = tmpcol.getBlue() / 255.0f; + } + + if (colors->get("box", &tmpcol)) + { + m_box_glow_color[0] = tmpcol.getRed() / 255.0f; + m_box_glow_color[1] = tmpcol.getGreen() / 255.0f; + m_box_glow_color[2] = tmpcol.getBlue() / 255.0f; + } + + if (colors->get("banana", &tmpcol)) + { + m_banana_glow_color[0] = tmpcol.getRed() / 255.0f; + m_banana_glow_color[1] = tmpcol.getGreen() / 255.0f; + m_banana_glow_color[2] = tmpcol.getBlue() / 255.0f; + } + + if (colors->get("bubblegum", &tmpcol)) + { + m_bubblegum_glow_color[0] = tmpcol.getRed() / 255.0f; + m_bubblegum_glow_color[1] = tmpcol.getGreen() / 255.0f; + m_bubblegum_glow_color[2] = tmpcol.getBlue() / 255.0f; + } + } + // Get the default KartProperties // ------------------------------ const XMLNode *node = root -> getNode("general-kart-defaults"); diff --git a/src/config/stk_config.hpp b/src/config/stk_config.hpp index 67b93140d..958201878 100644 --- a/src/config/stk_config.hpp +++ b/src/config/stk_config.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -148,6 +148,12 @@ public: * be generated. */ float m_replay_delta_angle; + /** Colors for glows */ + float m_nitro_glow_color[3]; + float m_box_glow_color[3]; + float m_banana_glow_color[3]; + float m_bubblegum_glow_color[3]; + private: /** True if stk_config has been loaded. This is necessary if the * --stk-config command line parameter has been specified to avoid diff --git a/src/config/user_config.cpp b/src/config/user_config.cpp index 48ef2c5b0..75868892d 100644 --- a/src/config/user_config.cpp +++ b/src/config/user_config.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // Modelled after Supertux's configfile.cpp // // This program is free software; you can redistribute it and/or @@ -182,6 +182,162 @@ void GroupUserConfigParam::addChild(UserConfigParam* child) } // addChild +// ============================================================================ +template +ListUserConfigParam::ListUserConfigParam(const char* param_name, + const char* comment) +{ + m_param_name = param_name; + all_params.push_back(this); + if(comment != NULL) m_comment = comment; +} // ListUserConfigParam + +// ============================================================================ +template +ListUserConfigParam::ListUserConfigParam(const char* param_name, + const char* comment, + int nb_elements, + ...) +{ + m_param_name = param_name; + all_params.push_back(this); + if(comment != NULL) m_comment = comment; + + // add the default list + va_list arguments; + va_start ( arguments, nb_elements ); + for ( int i = 0; i < nb_elements; i++ ) + m_elements.push_back(va_arg ( arguments, T )); + va_end ( arguments ); // Cleans up the list +} // ListUserConfigParam + +// ============================================================================ +template +ListUserConfigParam::ListUserConfigParam(const char* param_name, + GroupUserConfigParam* group, + const char* comment) +{ + m_param_name = param_name; + group->addChild(this); + if(comment != NULL) m_comment = comment; +} // ListUserConfigParam + +// ============================================================================ +template +ListUserConfigParam::ListUserConfigParam(const char* param_name, + GroupUserConfigParam* group, + const char* comment, + int nb_elements, + ...) +{ + m_param_name = param_name; + group->addChild(this); + if(comment != NULL) m_comment = comment; + + // add the default list + va_list arguments; + va_start ( arguments, nb_elements ); + for ( int i = 0; i < nb_elements; i++ ) + m_elements.push_back(va_arg ( arguments, T )); + va_end ( arguments ); // Cleans up the list +} // ListUserConfigParam + +// ---------------------------------------------------------------------------- +template +void ListUserConfigParam::write(XMLWriter& stream) const +{ + const int elts_amount = m_elements.size(); + + // comment + if(m_comment.size() > 0) stream << " \n <" << m_param_name.c_str() << "\n"; + + stream << L" Size=\"" << elts_amount << "\"\n"; + // actual elements + for (int n=0; n\n"; + stream << L" \n\n"; +} // write + +// ---------------------------------------------------------------------------- + +// Write your own convert function depending on the type of list you use. +void convert(std::string str, char** str2) +{ + *str2 = (char*)(malloc(str.size()+1)); + strcpy(*str2, str.c_str()); +} +// Write your own equals function depending on the type of list you use. +bool equals(char* str1, char* str2) +{ + return (strcmp(str1, str2) == 0); +} + +template +void ListUserConfigParam::findYourDataInAChildOf(const XMLNode* node) +{ + const XMLNode* child = node->getNode( m_param_name ); + if (child == NULL) + { + //std::cerr << "/!\\ User Config : Couldn't find parameter group " + // << paramName << std::endl; + return; + } + + int attr_count = 0; + child->get( "Size", &attr_count); + for (int n=0; nget( oss.str(), &str); + convert(str, &elt); + // check if the element is already there : + bool there = false; + for (unsigned int i = 0; i < m_elements.size(); i++) + { + if (equals(m_elements[i], elt)) + { + there = true; + break; + } + } + if (!there) + { + Log::info("ListUserConfigParam", "New data : %s, \"%s\"", str.c_str(), elt); + m_elements.push_back(elt); + } + } + +} // findYourDataInAChildOf + +// ---------------------------------------------------------------------------- +template +void ListUserConfigParam::findYourDataInAnAttributeOf(const XMLNode* node) +{ +} // findYourDataInAnAttributeOf + +// ---------------------------------------------------------------------------- +template +void ListUserConfigParam::addElement(T element) +{ + m_elements.push_back(element); +} // findYourDataInAnAttributeOf + +// ---------------------------------------------------------------------------- +template +irr::core::stringw ListUserConfigParam::toString() const +{ + return ""; +} // toString + + + // ============================================================================ IntUserConfigParam::IntUserConfigParam(int default_value, const char* param_name, @@ -717,6 +873,17 @@ bool UserConfig::loadConfig() return true; } // loadConfig +// ---------------------------------------------------------------------------- + +void UserConfig::postLoadInit() +{ + for (int i = 0; i < UserConfigParams::m_all_players.size(); i++) + { + PlayerProfile* player = UserConfigParams::m_all_players.get(i); + if (player->isGuestAccount()) player->setName(_LTR("Guest")); + } +} + // ---------------------------------------------------------------------------- /** Write settings to config file. */ void UserConfig::saveConfig() diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 47acee8ab..2638c739f 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // Modelled after Supertux's configfile.h // // This program is free software; you can redistribute it and/or @@ -99,6 +99,45 @@ public: irr::core::stringw toString() const; }; // GroupUserConfigParam +// ============================================================================ +template +class ListUserConfigParam : public UserConfigParam +{ + std::vector m_elements; + +public: + ListUserConfigParam(const char* param_name, + const char* comment = NULL); + ListUserConfigParam(const char* param_name, + const char* comment, + int nb_elts, + ...); + ListUserConfigParam(const char* param_name, + GroupUserConfigParam* group, + const char* comment = NULL); + ListUserConfigParam(const char* param_name, + GroupUserConfigParam* group, + const char* comment, + int nb_elts, + ...); + + void write(XMLWriter& stream) const; + void findYourDataInAChildOf(const XMLNode* node); + void findYourDataInAnAttributeOf(const XMLNode* node); + + void addElement(T element); + + irr::core::stringw toString() const; + + operator std::vector() const + { return m_elements; } + float& operator=(const std::vector& v) + { m_elements = std::vector(v); return m_elements; } + float& operator=(const ListUserConfigParam& v) + { m_elements = std::vector(v); return m_elements; } +}; // ListUserConfigParam +typedef ListUserConfigParam StringListUserConfigParam; + // ============================================================================ class IntUserConfigParam : public UserConfigParam { @@ -347,31 +386,31 @@ namespace UserConfigParams PARAM_DEFAULT( GroupUserConfigParam("WiiMote", "Settings for the wiimote") ); PARAM_PREFIX FloatUserConfigParam m_wiimote_raw_max - PARAM_DEFAULT( FloatUserConfigParam(25.0f, "wiimote-raw-max", + PARAM_DEFAULT( FloatUserConfigParam(20.0f, "wiimote-raw-max", &m_wiimote_group, "At what raw input value maximum steering is reached (between 1 and 25).") ); - PARAM_PREFIX FloatUserConfigParam m_wiimote_weight_linear - PARAM_DEFAULT( FloatUserConfigParam(0.2f, "wiimote-weight-linear", + PARAM_PREFIX FloatUserConfigParam m_wiimote_weight_linear + PARAM_DEFAULT( FloatUserConfigParam(1.0f, "wiimote-weight-linear", &m_wiimote_group, - "A weight applied to the linear component of mapping wiimote angle to steering angle")); + "A weight applied to the linear component of mapping wiimote angle to steering angle")); PARAM_PREFIX FloatUserConfigParam m_wiimote_weight_square - PARAM_DEFAULT( FloatUserConfigParam(0.8f, "wiimote-weight-square", + PARAM_DEFAULT( FloatUserConfigParam(0.0f, "wiimote-weight-square", &m_wiimote_group, - "A weight applied to the square component of mapping wiimote angle to steering angle")); + "A weight applied to the square component of mapping wiimote angle to steering angle")); PARAM_PREFIX FloatUserConfigParam m_wiimote_weight_asin PARAM_DEFAULT( FloatUserConfigParam(0.0f, "wiimote-weight-asin", &m_wiimote_group, - "A weight applied to the asin component of mapping wiimote angle to steering angle")); + "A weight applied to the asin component of mapping wiimote angle to steering angle")); PARAM_PREFIX FloatUserConfigParam m_wiimote_weight_sin PARAM_DEFAULT( FloatUserConfigParam(0.0f, "wiimote-weight-sin", &m_wiimote_group, - "A weight applied to the sin component of mapping wiimote angle to steering angle")); + "A weight applied to the sin component of mapping wiimote angle to steering angle")); - // ---- GP start order + // ---- GP start order PARAM_PREFIX GroupUserConfigParam m_gp_start_order PARAM_DEFAULT( GroupUserConfigParam("GpStartOrder", "Order karts start in GP") ); @@ -446,8 +485,9 @@ namespace UserConfigParams /** True if check structures should be debugged. */ PARAM_PREFIX bool m_check_debug PARAM_DEFAULT( false ); - /** Special debug camera being high over the kart. */ - PARAM_PREFIX bool m_camera_debug PARAM_DEFAULT( false ); + /** Special debug camera: 0: normal cameral; 1: being high over the kart.; + 2: on ground level. */ + PARAM_PREFIX int m_camera_debug PARAM_DEFAULT( false ); /** True if physics debugging should be enabled. */ PARAM_PREFIX bool m_physics_debug PARAM_DEFAULT( false ); @@ -479,18 +519,49 @@ namespace UserConfigParams /** True if hardware skinning should be enabled */ PARAM_PREFIX bool m_hw_skinning_enabled PARAM_DEFAULT( false ); - /** True if Christmas Mode should be enabled */ - PARAM_PREFIX bool m_xmas_enabled PARAM_DEFAULT( false ); - // not saved to file // ---- Networking - PARAM_PREFIX StringUserConfigParam m_server_address - PARAM_DEFAULT( StringUserConfigParam("localhost", "server_adress", - "Information about last server used") ); PARAM_PREFIX IntUserConfigParam m_server_port - PARAM_DEFAULT( IntUserConfigParam(2305, "server_port", - "Information about last server used") ); + PARAM_DEFAULT( IntUserConfigParam(7321, "server_port", + "Information about the port to listen on.") ); + + PARAM_PREFIX IntUserConfigParam m_server_max_players + PARAM_DEFAULT( IntUserConfigParam(16, "server_max_players", + "Maximum number of players on the server.") ); + + PARAM_PREFIX StringListUserConfigParam m_stun_servers + PARAM_DEFAULT( StringListUserConfigParam("Stun_servers", "The stun servers" + " that will be used to know the public address.", + 24, + "provserver.televolution.net", + "sip1.lakedestiny.cordiaip.com", + "stun1.voiceeclipse.net", + "stun01.sipphone.com", + "stun.callwithus.com", + "stun.counterpath.net", + "stun.endigovoip.com", + "stun.ekiga.net", + "stun.ideasip.com" , + "stun.internetcalls.com", + "stun.ipns.com", + "stun.noc.ams-ix.net", + "stun.phonepower.com", + "stun.phoneserve.com", + "stun.rnktel.com", + "stun.softjoys.com", + "stunserver.org", + "stun.sipgate.net", + "stun.stunprotocol.org", + "stun.voip.aebc.com", + "stun.voipbuster.com", + "stun.voxalot.com", + "stun.voxgratia.org", + "stun.xten.com") ); + + PARAM_PREFIX StringUserConfigParam m_packets_log_filename + PARAM_DEFAULT( StringUserConfigParam("packets_log.txt", "packets_log_filename", + "Where to log received and sent packets.") ); // ---- Graphic Quality PARAM_PREFIX GroupUserConfigParam m_graphics_quality @@ -509,14 +580,15 @@ namespace UserConfigParams #define FBO_DEFAULT true #endif - PARAM_PREFIX BoolUserConfigParam m_fbo - PARAM_DEFAULT( BoolUserConfigParam(FBO_DEFAULT, "fbo", - &m_graphics_quality, "Use frame buffer objects (FBOs)") ); - PARAM_PREFIX BoolUserConfigParam m_graphical_effects PARAM_DEFAULT( BoolUserConfigParam(true, "anim_gfx", &m_graphics_quality, "Scenery animations") ); + // This saves the actual user preference. + PARAM_PREFIX IntUserConfigParam m_xmas_mode + PARAM_DEFAULT( IntUserConfigParam(0, "christmas-mode", + &m_graphics_quality, "Christmas hats: 0 use calendar, 1 always on, 2 always off") ); + PARAM_PREFIX BoolUserConfigParam m_weather_effects PARAM_DEFAULT( BoolUserConfigParam(true, "weather_gfx", &m_graphics_quality, "Weather effects") ); @@ -534,10 +606,12 @@ namespace UserConfigParams &m_graphics_quality, "Whether trilinear filtering is allowed to be " "used (true or false)") ); + /* PARAM_PREFIX IntUserConfigParam m_antialiasing PARAM_DEFAULT( IntUserConfigParam(0, "antialiasing", &m_graphics_quality, "Whether antialiasing is enabled (0 = disabled, 1 = 2x, 2 = 4x, 3 = 8x") ); + */ PARAM_PREFIX BoolUserConfigParam m_vsync PARAM_DEFAULT( BoolUserConfigParam(false, "vsync", &m_graphics_quality, @@ -546,18 +620,27 @@ namespace UserConfigParams PARAM_DEFAULT( BoolUserConfigParam(true, "pixel_shaders", &m_graphics_quality, "Whether to enable pixel shaders (splatting, normal maps, ...)") ); - PARAM_PREFIX BoolUserConfigParam m_postprocess_enabled + PARAM_PREFIX BoolUserConfigParam m_motionblur PARAM_DEFAULT( BoolUserConfigParam(false, - "postprocess_enabled", &m_graphics_quality, - "Whether post-processing (motion blur...) should " - "be enabled") ); + "motionblur_enabled", &m_graphics_quality, + "Whether motion blur should be enabled") ); + PARAM_PREFIX BoolUserConfigParam m_mlaa + PARAM_DEFAULT( BoolUserConfigParam(false, + "mlaa", &m_graphics_quality, + "Whether MLAA anti-aliasing should be enabled") ); + PARAM_PREFIX IntUserConfigParam m_ssao + PARAM_DEFAULT( IntUserConfigParam(0, + "ssao", &m_graphics_quality, + "Whether SSAO is enabled (0 = disabled, 1 = low, 2 = high") ); + PARAM_PREFIX IntUserConfigParam m_shadows + PARAM_DEFAULT( IntUserConfigParam(0, + "shadows", &m_graphics_quality, + "Whether shadows are enabled (0 = disabled, 1 = low, 2 = high") ); // ---- Misc PARAM_PREFIX BoolUserConfigParam m_cache_overworld PARAM_DEFAULT( BoolUserConfigParam(true, "cache-overworld") ); - PARAM_PREFIX BoolUserConfigParam m_minimal_race_gui - PARAM_DEFAULT( BoolUserConfigParam(false, "minimal-race-gui") ); // TODO : is this used with new code? does it still work? PARAM_PREFIX BoolUserConfigParam m_crashed PARAM_DEFAULT( BoolUserConfigParam(false, "crashed") ); @@ -601,9 +684,48 @@ namespace UserConfigParams PARAM_DEFAULT( WStringUserConfigParam(L"", "default_player", "Which player to use by default (if empty, will prompt)") ); + // ---- Internet related + + PARAM_PREFIX IntUserConfigParam m_internet_status + PARAM_DEFAULT( IntUserConfigParam(0, "enable_internet", + "Status of internet: 0 user " + "wasn't asked, 1: allowed, 2: " + "not allowed") ); + + // ---- Online gameplay related + + PARAM_PREFIX GroupUserConfigParam m_online_group + PARAM_DEFAULT( GroupUserConfigParam("OnlinePlay", + "Everything related to online play.") ); + + PARAM_PREFIX StringUserConfigParam m_server_multiplayer + PARAM_DEFAULT( StringUserConfigParam( "https://api.stkaddons.net/", + "server_multiplayer", + &m_online_group, + "The server used for online multiplayer.")); + + PARAM_PREFIX BoolUserConfigParam m_saved_session + PARAM_DEFAULT( BoolUserConfigParam( false, + "saved_session", + &m_online_group, + "Is there a saved session?") ); + + PARAM_PREFIX IntUserConfigParam m_saved_user + PARAM_DEFAULT( IntUserConfigParam( 0, + "saved_user", + &m_online_group, + "User ID of the saved session.") ); + + PARAM_PREFIX StringUserConfigParam m_saved_token + PARAM_DEFAULT( StringUserConfigParam( "", + "saved_token", + &m_online_group, + "Token of the saved session.") ); + + // ---- Addon server related entries PARAM_PREFIX GroupUserConfigParam m_addon_group - PARAM_DEFAULT( GroupUserConfigParam("AddonAndNews", + PARAM_DEFAULT( GroupUserConfigParam("AddonAndNews", "Addon and news related settings") ); PARAM_PREFIX StringUserConfigParam m_server_addons @@ -634,13 +756,6 @@ namespace UserConfigParams "Don't show important message " "with this or a lower id again") ); - PARAM_PREFIX IntUserConfigParam m_internet_status - PARAM_DEFAULT( IntUserConfigParam(0, "enable_internet", - &m_addon_group, - "Status of internet: 0 user " - "wasn't asked, 1: allowed, 2: " - "not allowed") ); - PARAM_PREFIX TimeUserConfigParam m_addons_last_updated PARAM_DEFAULT( TimeUserConfigParam(0, "addon_last_updated", &m_addon_group, @@ -713,7 +828,7 @@ public: const irr::core::stringw& getWarning() { return m_warning; } void resetWarning() { m_warning=""; } void setWarning(irr::core::stringw& warning) { m_warning=warning; } - + void postLoadInit(); void addDefaultPlayer(); }; // UserConfig diff --git a/src/graphics/callbacks.cpp b/src/graphics/callbacks.cpp new file mode 100644 index 000000000..49fe5d779 --- /dev/null +++ b/src/graphics/callbacks.cpp @@ -0,0 +1,795 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 "graphics/callbacks.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/wind.hpp" +#include "guiengine/engine.hpp" +#include "modes/world.hpp" +#include "tracks/track.hpp" +#include "utils/helpers.hpp" + +using namespace video; +using namespace core; + +//------------------------------------- + +void NormalMapProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + s32 decaltex = 0; + srv->setPixelShaderConstant("DecalTex", &decaltex, 1); + + s32 bumptex = 1; + srv->setPixelShaderConstant("BumpTex", &bumptex, 1); + + s32 lightmapTex = (m_with_lightmap ? 2 : 0); + srv->setPixelShaderConstant("LightMapTex", &lightmapTex, 1); + + s32 hasLightMap = (m_with_lightmap ? 1 : 0); + srv->setPixelShaderConstant("HasLightMap", &hasLightMap, 1); + + // We could calculate light direction as coming from the sun (then we'd need to + // transform it into camera space). But I find that pretending light + // comes from the camera gives good results + const float lightdir[] = {0.1852f, -0.1852f, -0.9259f}; + srv->setVertexShaderConstant("lightdir", lightdir, 3); + + + firstdone = true; + } +} + +//------------------------------------- + +void WaterShaderProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + float strength = time; + strength = 1.4f - fabsf(noise2d(strength / 30.0f + 133)) * 0.8f; + + m_dx_1 += GUIEngine::getLatestDt() * m_water_shader_speed_1 * strength; + m_dy_1 += GUIEngine::getLatestDt() * m_water_shader_speed_1 * strength; + + m_dx_2 += GUIEngine::getLatestDt() * m_water_shader_speed_2 * strength; + m_dy_2 -= GUIEngine::getLatestDt() * m_water_shader_speed_2 * strength; + + if (m_dx_1 > 1.0f) m_dx_1 -= 1.0f; + if (m_dy_1 > 1.0f) m_dy_1 -= 1.0f; + if (m_dx_2 > 1.0f) m_dx_2 -= 1.0f; + if (m_dy_2 < 0.0f) m_dy_2 += 1.0f; + + const float d1[2] = { m_dx_1, m_dy_1 }; + const float d2[2] = { m_dx_2, m_dy_2 }; + + srv->setVertexShaderConstant("delta1", d1, 2); + srv->setVertexShaderConstant("delta2", d2, 2); + + const float speed = irr_driver->getDevice()->getTimer()->getTime() / m_speed; + const float height = m_height * strength; + + srv->setVertexShaderConstant("height", &height, 1); + srv->setVertexShaderConstant("speed", &speed, 1); + srv->setVertexShaderConstant("waveLength", &m_length, 1); + + // Can't use the firstdone optimization, as the callback is shared + //if (!firstdone) + { + s32 decaltex = 0; + srv->setPixelShaderConstant("DecalTex", &decaltex, 1); + + s32 bumptex = 1; + srv->setPixelShaderConstant("BumpTex1", &bumptex, 1); + + bumptex = 2; + srv->setPixelShaderConstant("BumpTex2", &bumptex, 1); + + // Calculate light direction as coming from the sun. + matrix4 normalm = srv->getVideoDriver()->getTransform(ETS_VIEW); + normalm.makeInverse(); + normalm = normalm.getTransposed(); + vector3df tmp = m_sunpos; + normalm.transformVect(tmp); + tmp.normalize(); + + const float lightdir[] = {tmp.X, tmp.Y, tmp.Z}; + srv->setVertexShaderConstant("lightdir", lightdir, 3); + + firstdone = true; + } +} + +//------------------------------------- + +void GrassShaderProvider::OnSetConstants(IMaterialRendererServices *srv, int userData) +{ + IVideoDriver * const drv = srv->getVideoDriver(); + const core::vector3df pos = drv->getTransform(ETS_WORLD).getTranslation(); + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + float strength = (pos.X + pos.Y + pos.Z) * 1.2f + time * m_speed; + strength = noise2d(strength / 10.0f) * m_amplitude * 5; + // * 5 is to work with the existing amplitude values. + + // Pre-multiply on the cpu + vector3df wind = irr_driver->getWind() * strength; + + srv->setVertexShaderConstant("windDir", &wind.X, 3); + + if (!firstdone) + { + s32 tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void ColorLevelsProvider::OnSetConstants(IMaterialRendererServices *srv, int userData) +{ + + m_inlevel = World::getWorld()->getTrack()->getColorLevelIn(); + m_outlevel = World::getWorld()->getTrack()->getColorLevelOut(); + + srv->setVertexShaderConstant("inlevel", &m_inlevel.X, 3); + srv->setVertexShaderConstant("outlevel", &m_outlevel.X, 2); + + if (!firstdone) + { + s32 tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void SplattingProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float camfar = irr_driver->getSceneManager()->getActiveCamera()->getFarValue(); + srv->setVertexShaderConstant("far", &camfar, 1); + + // The normal is transformed by the inverse transposed world matrix + // because we want world-space normals + matrix4 invtworldm = irr_driver->getVideoDriver()->getTransform(ETS_WORLD); + invtworldm.makeInverse(); + invtworldm = invtworldm.getTransposed(); + + srv->setVertexShaderConstant("invtworldm", invtworldm.pointer(), 16); + + float objectid = 0; + const stringc name = mat.TextureLayer[0].Texture->getName().getPath(); + objectid = shash8((const u8 *) name.c_str(), name.size()) / 255.0f; + srv->setVertexShaderConstant("objectid", &objectid, 1); + + if (!firstdone) + { + s32 tex_layout = 1; + srv->setPixelShaderConstant("tex_layout", &tex_layout, 1); + + s32 tex_detail0 = 2; + srv->setPixelShaderConstant("tex_detail0", &tex_detail0, 1); + + s32 tex_detail1 = 3; + srv->setPixelShaderConstant("tex_detail1", &tex_detail1, 1); + + s32 tex_detail2 = 4; + srv->setPixelShaderConstant("tex_detail2", &tex_detail2, 1); + + s32 tex_detail3 = 5; + srv->setPixelShaderConstant("tex_detail3", &tex_detail3, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void BubbleEffectProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float start = fabsf(mat.MaterialTypeParam2); + const bool visible = mat.MaterialTypeParam2 > 0; + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + float transparency; + + const float diff = (time - start) / 3.0f; + + if (visible) + { + transparency = diff; + } + else + { + transparency = 1.0f - diff; + } + + transparency = clampf(transparency, 0, 1); + + srv->setVertexShaderConstant("time", &time, 1); + srv->setVertexShaderConstant("transparency", &transparency, 1); +} + +//------------------------------------- + +void RainEffectProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float screenw = (float)UserConfigParams::m_width; + const float time = irr_driver->getDevice()->getTimer()->getTime() / 90.0f; + const matrix4 viewm = srv->getVideoDriver()->getTransform(ETS_VIEW); + const vector3df campos = irr_driver->getSceneManager()->getActiveCamera()->getPosition(); + + srv->setVertexShaderConstant("screenw", &screenw, 1); + srv->setVertexShaderConstant("time", &time, 1); + srv->setVertexShaderConstant("viewm", viewm.pointer(), 16); + srv->setVertexShaderConstant("campos", &campos.X, 3); +} + +//------------------------------------- + +void SnowEffectProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + srv->setVertexShaderConstant("time", &time, 1); +} + +//------------------------------------- + +void MotionBlurProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + // We need the maximum texture coordinates: + float max_tex_height = m_maxheight[m_current_camera]; + srv->setPixelShaderConstant("max_tex_height", &max_tex_height, 1); + + // Scale the boost time to get a usable boost amount: + float boost_amount = m_boost_time[m_current_camera] * 0.7f; + + // Especially for single screen the top of the screen is less blurred + // in the fragment shader by multiplying the blurr factor by + // (max_tex_height - texcoords.t), where max_tex_height is the maximum + // texture coordinate (1.0 or 0.5). In split screen this factor is too + // small (half the value compared with non-split screen), so we + // multiply this by 2. + if(Camera::getNumCameras() > 1) + boost_amount *= 2.0f; + + srv->setPixelShaderConstant("boost_amount", &boost_amount, 1); + srv->setPixelShaderConstant("center", + &(m_center[m_current_camera].X), 2); + srv->setPixelShaderConstant("direction", + &(m_direction[m_current_camera].X), 2); + + // Use a radius of 0.15 when showing a single kart, otherwise (2-4 karts + // on splitscreen) use only 0.75. + float radius = Camera::getNumCameras()==1 ? 0.15f : 0.075f; + srv->setPixelShaderConstant("mask_radius", &radius, 1); + + const int texunit = 0; + srv->setPixelShaderConstant("color_buffer", &texunit, 1); +} + +//------------------------------------- + +void GaussianBlurProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("pixel", m_pixel, 2); +} + +//------------------------------------- + +void MipVizProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const ITexture * const tex = mat.TextureLayer[0].Texture; + + const int notex = (mat.TextureLayer[0].Texture == NULL); + srv->setVertexShaderConstant("notex", ¬ex, 1); + if (!tex) return; + + const dimension2du size = tex->getSize(); + + const float texsize[2] = { + (float)size.Width, + (float)size.Height + }; + + srv->setVertexShaderConstant("texsize", texsize, 2); +} + +//------------------------------------- + +void ColorizeProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("col", m_color, 3); +} + +//------------------------------------- + +void GlowProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("res", m_res, 2); +} + +//------------------------------------- + +void ObjectPassProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float camfar = irr_driver->getSceneManager()->getActiveCamera()->getFarValue(); + srv->setVertexShaderConstant("far", &camfar, 1); + + // The normal is transformed by the inverse transposed world matrix + // because we want world-space normals + matrix4 invtworldm = irr_driver->getVideoDriver()->getTransform(ETS_WORLD); + invtworldm.makeInverse(); + invtworldm = invtworldm.getTransposed(); + + srv->setVertexShaderConstant("invtworldm", invtworldm.pointer(), 16); + + const int hastex = mat.TextureLayer[0].Texture != NULL; + srv->setVertexShaderConstant("hastex", &hastex, 1); + + const int haslightmap = mat.TextureLayer[1].Texture != NULL; + srv->setVertexShaderConstant("haslightmap", &haslightmap, 1); + + float objectid = 0; + if (hastex) + { + const stringc name = mat.TextureLayer[0].Texture->getName().getPath(); + objectid = shash8((const u8 *) name.c_str(), name.size()) / 255.0f; + } + srv->setVertexShaderConstant("objectid", &objectid, 1); + + //if (!firstdone) + // Can't use the firstdone optimization, as this callback is used for multiple shaders + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("lighttex", &tex, 1); + } +} + +//------------------------------------- + +void LightBlendProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const SColorf s = irr_driver->getSceneManager()->getAmbientLight(); + + float ambient[3] = { s.r, s.g, s.b }; + srv->setVertexShaderConstant("ambient", ambient, 3); + + int spectex = 1; + srv->setVertexShaderConstant("spectex", &spectex, 1); +} + +//------------------------------------- + +void PointLightProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("screen", m_screen, 2); + srv->setVertexShaderConstant("spec", &m_specular, 1); + srv->setVertexShaderConstant("col", m_color, 3); + srv->setVertexShaderConstant("campos", m_campos, 3); + srv->setVertexShaderConstant("center", m_pos, 3); + srv->setVertexShaderConstant("r", &m_radius, 1); + srv->setVertexShaderConstant("invprojview", m_invprojview.pointer(), 16); + + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("ntex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("dtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void SunLightProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const int hasclouds = World::getWorld()->getTrack()->hasClouds() && + UserConfigParams::m_weather_effects; + + srv->setVertexShaderConstant("screen", m_screen, 2); + srv->setVertexShaderConstant("col", m_color, 3); + srv->setVertexShaderConstant("center", m_pos, 3); + srv->setVertexShaderConstant("invprojview", m_invprojview.pointer(), 16); + srv->setVertexShaderConstant("hasclouds", &hasclouds, 1); + + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + float strength = time; + strength = fabsf(noise2d(strength / 10.0f)) * 0.003f; + + const vector3df winddir = irr_driver->getWind() * strength; + m_wind[0] += winddir.X; + m_wind[1] += winddir.Z; + srv->setVertexShaderConstant("wind", m_wind, 2); + + if (UserConfigParams::m_shadows) + { + srv->setVertexShaderConstant("shadowmat", m_shadowmat.pointer(), 16); + } + + // Can't use the firstdone optimization, as this callback is used for multiple shaders + //if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("ntex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("dtex", &tex, 1); + + tex = 2; + srv->setVertexShaderConstant("cloudtex", &tex, 1); + + tex = 3; + srv->setVertexShaderConstant("shadowtex", &tex, 1); + + tex = 4; + srv->setVertexShaderConstant("warpx", &tex, 1); + + tex = 5; + srv->setVertexShaderConstant("warpy", &tex, 1); + + const float shadowoffset = 1.0f / irr_driver->getRTT(RTT_SHADOW)->getSize().Width; + srv->setVertexShaderConstant("shadowoffset", &shadowoffset, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void BloomProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("low", &m_threshold, 1); +} + +//------------------------------------- + +void MLAAColor1Provider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + const float pixels[2] = { + 1.0f / UserConfigParams::m_width, + 1.0f / UserConfigParams::m_height + }; + + srv->setPixelShaderConstant("PIXEL_SIZE", pixels, 2); + + firstdone = true; + } +} + +//------------------------------------- + +void MLAABlend2Provider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + const float pixels[2] = { + 1.0f / UserConfigParams::m_width, + 1.0f / UserConfigParams::m_height + }; + + srv->setPixelShaderConstant("PIXEL_SIZE", pixels, 2); + + + int tex = 0; + srv->setPixelShaderConstant("edgesMap", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("areaMap", &tex, 1); + + + firstdone = true; + } +} + +//------------------------------------- + +void MLAANeigh3Provider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + const float pixels[2] = { + 1.0f / UserConfigParams::m_width, + 1.0f / UserConfigParams::m_height + }; + + srv->setPixelShaderConstant("PIXEL_SIZE", pixels, 2); + + + int tex = 0; + srv->setPixelShaderConstant("blendMap", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("colorMap", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void SSAOProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + int tex = 0; + srv->setPixelShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("oldtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void GodRayProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setPixelShaderConstant("sunpos", m_sunpos, 2); +} + +//------------------------------------- + +void ShadowPassProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const int hastex = mat.TextureLayer[0].Texture != NULL; + srv->setVertexShaderConstant("hastex", &hastex, 1); + + int viz = irr_driver->getShadowViz(); + srv->setVertexShaderConstant("viz", &viz, 1); + + int wireframe = mat.Wireframe; + srv->setVertexShaderConstant("wireframe", &wireframe, 1); + + float objectid = 0; + if (hastex) + { + const stringc name = mat.TextureLayer[0].Texture->getName().getPath(); + objectid = shash8((const u8 *) name.c_str(), name.size()) / 255.0f; + } + srv->setVertexShaderConstant("objectid", &objectid, 1); + + //if (!firstdone) + // Can't use the firstdone optimization, as this callback is used for multiple shaders + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("warpx", &tex, 1); + tex = 2; + srv->setVertexShaderConstant("warpy", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void ShadowImportanceProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("shadowmat", m_shadowmat.pointer(), 16); + srv->setVertexShaderConstant("ipvmat", m_invprojview.pointer(), 16); + + srv->setVertexShaderConstant("campos", m_campos, 3); + + int low = UserConfigParams::m_shadows == 1; + srv->setVertexShaderConstant("low", &low, 1); + + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("ntex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("dtex", &tex, 1); + + tex = 2; + srv->setVertexShaderConstant("ctex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void CollapseProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("pixel", m_pixel, 2); + srv->setVertexShaderConstant("multi", m_multi, 2); + srv->setVertexShaderConstant("size", &m_size, 1); + + //if (!firstdone) + // Can't use the firstdone optimization, as this callback is used for multiple shaders + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("oldtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void BloomPowerProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("power", &m_power, 1); +} + +//------------------------------------- + +void MultiplyProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("tex1", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("tex2", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void ShadowGenProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("halft", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("quarter", &tex, 1); + + tex = 2; + srv->setVertexShaderConstant("eighth", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void CausticsProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + const float speed = World::getWorld()->getTrack()->getCausticsSpeed(); + + float strength = time; + strength = fabsf(noise2d(strength / 10.0f)) * 0.006f + 0.001f; + + vector3df wind = irr_driver->getWind() * strength * speed; + m_dir[0] += wind.X; + m_dir[1] += wind.Z; + + strength = time * 0.56f + sinf(time); + strength = fabsf(noise2d(0.0, strength / 6.0f)) * 0.0095f + 0.001f; + + wind = irr_driver->getWind() * strength * speed; + wind.rotateXZBy(cosf(time)); + m_dir2[0] += wind.X; + m_dir2[1] += wind.Z; + + srv->setVertexShaderConstant("dir", m_dir, 2); + srv->setVertexShaderConstant("dir2", m_dir2, 2); + + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("caustictex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void DisplaceProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + const float speed = World::getWorld()->getTrack()->getDisplacementSpeed(); + + float strength = time; + strength = fabsf(noise2d(strength / 10.0f)) * 0.006f + 0.002f; + + vector3df wind = irr_driver->getWind() * strength * speed; + m_dir[0] += wind.X; + m_dir[1] += wind.Z; + + strength = time * 0.56f + sinf(time); + strength = fabsf(noise2d(0.0, strength / 6.0f)) * 0.0095f + 0.0025f; + + wind = irr_driver->getWind() * strength * speed; + wind.rotateXZBy(cosf(time)); + m_dir2[0] += wind.X; + m_dir2[1] += wind.Z; + + srv->setVertexShaderConstant("dir", m_dir, 2); + srv->setVertexShaderConstant("dir2", m_dir2, 2); + + srv->setVertexShaderConstant("screen", m_screen, 2); +} + +//------------------------------------- + +void PPDisplaceProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + int viz = irr_driver->getDistortViz(); + srv->setPixelShaderConstant("viz", &viz, 1); + + if (!firstdone) + { + int tex = 0; + srv->setPixelShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("dtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void FogProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const Track * const track = World::getWorld()->getTrack(); + + // This function is only called once per frame - thus no need for setters. + const float fogmax = track->getFogMax(); + const float startH = track->getFogStartHeight(); + const float endH = track->getFogEndHeight(); + const float start = track->getFogStart(); + const float end = track->getFogEnd(); + const SColor tmpcol = track->getFogColor(); + + const float col[3] = { tmpcol.getRed() / 255.0f, + tmpcol.getGreen() / 255.0f, + tmpcol.getBlue() / 255.0f }; + + srv->setPixelShaderConstant("fogmax", &fogmax, 1); + srv->setPixelShaderConstant("startH", &startH, 1); + srv->setPixelShaderConstant("endH", &endH, 1); + srv->setPixelShaderConstant("start", &start, 1); + srv->setPixelShaderConstant("end", &end, 1); + srv->setPixelShaderConstant("col", col, 3); + srv->setVertexShaderConstant("ipvmat", m_invprojview.pointer(), 16); + srv->setVertexShaderConstant("campos", m_campos, 3); +} diff --git a/src/graphics/callbacks.hpp b/src/graphics/callbacks.hpp new file mode 100644 index 000000000..341b02e83 --- /dev/null +++ b/src/graphics/callbacks.hpp @@ -0,0 +1,729 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 HEADER_CALLBACKS_HPP +#define HEADER_CALLBACKS_HPP + +#include "config/user_config.hpp" +#include "graphics/irr_driver.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using namespace irr; + +class CallBase: public video::IShaderConstantSetCallBack +{ +public: + CallBase() + { + firstdone = 0; + } + + virtual void OnSetMaterial(const video::SMaterial &material) + { + mat = material; + } + +protected: + bool firstdone; + video::SMaterial mat; +}; + +// + +class NormalMapProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + NormalMapProvider(bool withLightmap) + { + m_with_lightmap = withLightmap; + } + +private: + bool m_with_lightmap; +}; + +// + +class WaterShaderProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setSpeed(const float s1, const float s2) + { + m_water_shader_speed_1 = s1; + m_water_shader_speed_2 = s2; + } + + WaterShaderProvider() + { + m_dx_1 = 0.0f; + m_dx_2 = 0.0f; + m_dy_1 = 0.0f; + m_dy_2 = 0.0f; + + m_water_shader_speed_1 = + m_water_shader_speed_2 = 0.0f; + } + + void setSunPosition(const core::vector3df &in) + { + m_sunpos = in; + m_sunpos.normalize(); + } + + void setSpeed(float speed) { m_speed = speed; } + void setHeight(float height) { m_height = height; } + void setLength(float length) { m_length = length; } + +private: + core::vector3df m_sunpos; + + float m_dx_1, m_dy_1, m_dx_2, m_dy_2; + float m_water_shader_speed_1; + float m_water_shader_speed_2; + + float m_speed; + float m_height; + float m_length; +}; + +// + +class GrassShaderProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + GrassShaderProvider() + { + m_amplitude = + m_speed = 0.0f; + } + + void setSpeed(float speed) + { + m_speed = speed; + } + + void setAmplitude(float amp) + { + m_amplitude = amp; + } + +private: + float m_amplitude, m_speed; +}; + +// + +class ColorLevelsProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + + ColorLevelsProvider() + { + } + +private: + core::vector3df m_inlevel; + core::vector2df m_outlevel; +}; + +// + +class SplattingProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class BubbleEffectProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + BubbleEffectProvider() + { + } + + // We hijack the material type param 2 of bubbles. + // It's time to start the fade, negative if fade out, positive if in. + // It'd be unused otherwise. + + void onMadeVisible(scene::IMeshBuffer * const mb) + { + if (!contains(mb)) + return; + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + } + + void onHidden(scene::IMeshBuffer * const mb) + { + if (!contains(mb)) + return; + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = irr_driver->getDevice()->getTimer()->getTime() / -1000.0f; + } + + void isInitiallyHidden(scene::IMeshBuffer * const mb) + { + if (!contains(mb)) + return; + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = irr_driver->getDevice()->getTimer()->getTime() / -1000.0f; + } + + void removeBubble(const scene::IMeshBuffer * const mb) + { + m_bubbles.erase(mb); + } + + void addBubble(scene::IMeshBuffer * const mb) + { + m_bubbles.insert(mb); + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = 1; + } + + bool contains(const scene::IMeshBuffer * const mb) const + { + return m_bubbles.count(mb)!=0; + } + +private: + std::set m_bubbles; +}; + +// + +class RainEffectProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class SnowEffectProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class MotionBlurProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setMaxHeight(u32 who, float height) + { + assert(who < MAX_PLAYER_COUNT); + m_maxheight[who] = height; + } + + void setBoostTime(u32 who, float time) + { + assert(who < MAX_PLAYER_COUNT); + m_boost_time[who] = time; + } + + void setCenter(u32 who, float X, float Y) + { + assert(who < MAX_PLAYER_COUNT); + m_center[who].X = X; + m_center[who].Y = Y; + } + + void setDirection(u32 who, float X, float Y) + { + assert(who < MAX_PLAYER_COUNT); + m_direction[who].X = X; + m_direction[who].Y = Y; + } + + void setCurrentCamera(u32 who) + { + m_current_camera = who; + } + +private: + float m_maxheight[MAX_PLAYER_COUNT]; + u32 m_current_camera; + float m_boost_time[MAX_PLAYER_COUNT]; + core::vector2df m_center[MAX_PLAYER_COUNT]; + core::vector2df m_direction[MAX_PLAYER_COUNT]; +}; + +// + +class GaussianBlurProvider: public CallBase +{ +public: + GaussianBlurProvider() + { + m_pixel[0] = 1.0f / UserConfigParams::m_width; + m_pixel[1] = 1.0f / UserConfigParams::m_height; + } + + void setResolution(int x, int y) + { + m_pixel[0] = 1.0f / x; + m_pixel[1] = 1.0f / y; + } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + +private: + float m_pixel[2]; +}; + +// + +class MipVizProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class ColorizeProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setColor(float r, float g, float b) + { + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; + } + +private: + float m_color[3]; +}; + +// + +class GlowProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setResolution(int x, int y) + { + m_res[0] = (float)x; + m_res[1] = (float)y; + } + +private: + float m_res[2]; +}; + +// + +class ObjectPassProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class LightBlendProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class PointLightProvider: public CallBase +{ +public: + PointLightProvider() + { + m_screen[0] = (float)UserConfigParams::m_width; + m_screen[1] = (float)UserConfigParams::m_height; + + m_specular = 200; + } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setColor(float r, float g, float b) + { + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; + } + + void setPosition(float x, float y, float z) + { + m_pos[0] = x; + m_pos[1] = y; + m_pos[2] = z; + } + + void setRadius(float r) + { + m_radius = r; + } + + void setSpecular(float s) + { + m_specular = s; + } + + void updateIPVMatrix() + { + // Update the campos and IPV matrix, only once per frame since it's costly + const core::vector3df &campos = + irr_driver->getSceneManager()->getActiveCamera()->getAbsolutePosition(); + m_campos[0] = campos.X; + m_campos[1] = campos.Y; + m_campos[2] = campos.Z; + + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + +private: + core::matrix4 m_invprojview; + + float m_campos[3]; + float m_color[3]; + float m_pos[3]; + float m_screen[2]; + float m_radius; + float m_specular; +}; + +// + +class SunLightProvider: public CallBase +{ +public: + SunLightProvider() + { + m_screen[0] = (float)UserConfigParams::m_width; + m_screen[1] = (float)UserConfigParams::m_height; + + m_wind[0] = m_wind[1] = 0; + } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setColor(float r, float g, float b) + { + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; + } + + void setPosition(float x, float y, float z) + { + m_pos[0] = x; + m_pos[1] = y; + m_pos[2] = z; + } + + void updateIPVMatrix() + { + // Update the IPV matrix, only once per frame since it's costly + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + + void setShadowMatrix(const core::matrix4 &mat) + { + m_shadowmat = mat; + } + +private: + core::matrix4 m_invprojview, m_shadowmat; + float m_color[3]; + float m_pos[3]; + float m_screen[2]; + float m_wind[2]; +}; + +// + +class BloomProvider: public CallBase +{ +public: + BloomProvider() { m_threshold = 0.75f; } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setThreshold(const float f) { m_threshold = f; } + +private: + float m_threshold; +}; + +// + +class MLAAColor1Provider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class MLAABlend2Provider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class MLAANeigh3Provider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class SSAOProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class GodRayProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + // In texcoords + void setSunPosition(float x, float y) { m_sunpos[0] = x; m_sunpos[1] = y; } + +private: + float m_sunpos[2]; +}; + +// + +class ShadowPassProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class ShadowImportanceProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void updateIPVMatrix() + { + // Update the IPV matrix, only once per frame since it's costly + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + const core::vector3df &campos = + irr_driver->getSceneManager()->getActiveCamera()->getAbsolutePosition(); + m_campos[0] = campos.X; + m_campos[1] = campos.Y; + m_campos[2] = campos.Z; + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + + void setShadowMatrix(const core::matrix4 &mat) + { + m_shadowmat = mat; + } + +private: + core::matrix4 m_invprojview, m_shadowmat; + float m_campos[3]; +}; + +// + +class CollapseProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setResolution(const int x, const int y) + { + m_pixel[0] = 1.0f / x; + m_pixel[1] = 1.0f / y; + + m_multi[0] = m_multi[1] = 1; + + if (x < 2 || y < 2) + { + u32 i; + for (i = 0; i < 2; i++) + { + // No increase for the other direction + if (m_pixel[i] > 0.9f) m_pixel[i] = m_multi[i] = 0; + } + + std::swap(m_multi[0], m_multi[1]); + } + + m_size = (int)std::max(x, y); + } + +private: + float m_pixel[2]; + int m_size; + float m_multi[2]; +}; + +// + +class BloomPowerProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setPower(float power) + { + m_power = power / 10.0f; + } + +private: + float m_power; +}; + +// + +class MultiplyProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class ShadowGenProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class CausticsProvider: public CallBase +{ +public: + CausticsProvider() { m_dir[0] = m_dir[1] = m_dir2[0] = m_dir2[1] = 0; } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + +private: + float m_dir[2], m_dir2[2]; +}; + +// + +class DisplaceProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + DisplaceProvider() + { + m_screen[0] = (float)UserConfigParams::m_width; + m_screen[1] = (float)UserConfigParams::m_height; + + m_dir[0] = m_dir[1] = m_dir2[0] = m_dir2[1] = 0; + } + +private: + float m_screen[2]; + float m_dir[2], m_dir2[2]; +}; + +// + +class PPDisplaceProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class FogProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void updateIPVMatrix() + { + // Update the campos and IPV matrix, only once per frame since it's costly + const core::vector3df &campos = + irr_driver->getSceneManager()->getActiveCamera()->getAbsolutePosition(); + m_campos[0] = campos.X; + m_campos[1] = campos.Y; + m_campos[2] = campos.Z; + + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + +private: + core::matrix4 m_invprojview; + float m_campos[3]; +}; + +#endif diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index bc0e44b92..42946b1aa 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 SuperTuxKart-Team, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 SuperTuxKart-Team, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,11 +19,7 @@ #include "graphics/camera.hpp" -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include #include "audio/music_manager.hpp" #include "config/user_config.hpp" @@ -35,10 +31,12 @@ #include "karts/kart_properties.hpp" #include "karts/skidding.hpp" #include "modes/world.hpp" +#include "physics/btKart.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" #include "utils/aligned_array.hpp" #include "utils/constants.hpp" +#include "utils/vs.hpp" #include "ICameraSceneNode.h" #include "ISceneManager.h" @@ -387,13 +385,21 @@ void Camera::getCameraSettings(float *above_kart, float *cam_angle, // Fall through to falling mode. case CM_FALLING: { - *above_kart = 0.75f; + if(UserConfigParams::m_camera_debug==2) + { + *above_kart = 0; + *cam_angle = 0; + } + else + { + *above_kart = 0.75f; + *cam_angle = kp->getCameraForwardUpAngle(); + } float steering = m_kart->getSteerPercent() * (1.0f + (m_kart->getSkidding()->getSkidFactor() - 1.0f)/2.3f ); // quadratically to dampen small variations (but keep sign) float dampened_steer = fabsf(steering) * steering; - *cam_angle = kp->getCameraForwardUpAngle(); *sideway = -m_rotation_range*dampened_steer*0.5f; *distance = -m_distance; *smoothing = true; @@ -447,7 +453,7 @@ void Camera::update(float dt) // The following settings give a debug camera which shows the track from // high above the kart straight down. - if (UserConfigParams::m_camera_debug) + if (UserConfigParams::m_camera_debug==1) { core::vector3df xyz = m_kart->getXYZ().toIrrVector(); m_camera->setTarget(xyz); @@ -509,9 +515,10 @@ void Camera::positionCamera(float dt, float above_kart, float cam_angle, { Vec3 wanted_position; Vec3 wanted_target = m_kart->getXYZ(); - - wanted_target.setY(wanted_target.getY()+above_kart); - + if(UserConfigParams::m_camera_debug==2) + wanted_target.setY(m_kart->getVehicle()->getWheelInfo(2).m_raycastInfo.m_contactPointWS.getY()); + else + wanted_target.setY(wanted_target.getY()+above_kart); float tan_up = tan(cam_angle); Vec3 relative_position(side_way, fabsf(distance)*tan_up+above_kart, diff --git a/src/graphics/camera.hpp b/src/graphics/camera.hpp index 0e1240d17..01426c802 100644 --- a/src/graphics/camera.hpp +++ b/src/graphics/camera.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 SuperTuxKart-Team, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 SuperTuxKart-Team, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/explosion.cpp b/src/graphics/explosion.cpp index 028a37a20..c38c30e8f 100644 --- a/src/graphics/explosion.cpp +++ b/src/graphics/explosion.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// Copyright (C) 2004-2013 Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/explosion.hpp b/src/graphics/explosion.hpp index a5f848143..92ee65349 100644 --- a/src/graphics/explosion.hpp +++ b/src/graphics/explosion.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// Copyright (C) 2004-2013 Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/glow.cpp b/src/graphics/glow.cpp new file mode 100644 index 000000000..780c506b0 --- /dev/null +++ b/src/graphics/glow.cpp @@ -0,0 +1,85 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/glow.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +IMesh * GlowNode::sphere = NULL; +SMaterial GlowNode::mat; +aabbox3df GlowNode::box; + + +GlowNode::GlowNode(scene::ISceneManager* mgr, float radius): ISceneNode(mgr->getRootSceneNode(), mgr, -1) +{ + if (!sphere) + { + mat.Lighting = false; + mat.MaterialType = irr_driver->getShader(ES_GLOW); + + mat.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + mat.TextureLayer[0].TextureWrapU = + mat.TextureLayer[0].TextureWrapV = ETC_CLAMP_TO_EDGE; + mat.setFlag(EMF_TRILINEAR_FILTER, true); + mat.BlendOperation = EBO_ADD; + + sphere = mgr->getGeometryCreator()->createSphereMesh(1, 4, 4); + box = sphere->getBoundingBox(); + } + + setScale(vector3df(radius)); +} + +GlowNode::~GlowNode() +{ +} + +void GlowNode::render() +{ + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(mat); + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 0, ~0); + glEnable(GL_STENCIL_TEST); + + drv->drawMeshBuffer(sphere->getMeshBuffer(0)); + + glDisable(GL_STENCIL_TEST); +} + +void GlowNode::OnRegisterSceneNode() +{ + if (IsVisible) + { + SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT); + } + + ISceneNode::OnRegisterSceneNode(); +} diff --git a/src/network/race_start_message.hpp b/src/graphics/glow.hpp similarity index 55% rename from src/network/race_start_message.hpp rename to src/graphics/glow.hpp index eac2d7594..8d9fa12f9 100644 --- a/src/network/race_start_message.hpp +++ b/src/graphics/glow.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 Lauri Kasanen // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,25 +16,38 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_START_MESSAGE_HPP -#define HEADER_RACE_START_MESSAGE_HPP +#ifndef HEADER_GLOW_HPP +#define HEADER_GLOW_HPP -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" -#include "race/race_manager.hpp" +#include +#include -class RaceStartMessage : public Message +using namespace irr; + +// The actual node +class GlowNode: public scene::ISceneNode { -private: -// For now this is an empty message public: - RaceStartMessage() : Message(Message::MT_RACE_START) - { - allocate(0); - } // RaceStartMessage + GlowNode(scene::ISceneManager* mgr, float radius); + ~GlowNode(); - RaceStartMessage(ENetPacket* pkt):Message(pkt, MT_RACE_START) + virtual void render(); + + virtual const core::aabbox3d& getBoundingBox() const { + return box; } -}; // RaceStartMessage + + virtual void OnRegisterSceneNode(); + + virtual u32 getMaterialCount() const { return 1; } + virtual video::SMaterial& getMaterial(u32 i) { return mat; } + +private: + static video::SMaterial mat; + static core::aabbox3df box; + + static scene::IMesh *sphere; +}; + #endif diff --git a/src/graphics/glwrap.hpp b/src/graphics/glwrap.hpp new file mode 100644 index 000000000..fcac96621 --- /dev/null +++ b/src/graphics/glwrap.hpp @@ -0,0 +1,21 @@ +#ifndef GLWRAP_HEADER_H +#define GLWRAP_HEADER_H + +#if defined(__APPLE__) +# include +#elif defined(ANDROID) +# include +#elif defined(WIN32) +# define _WINSOCKAPI_ +// has to be included before gl.h because of WINGDIAPI and APIENTRY definitions +# include +# include +#else +# include +#endif + +// already includes glext.h, which defines useful GL constants. +// COpenGLDriver has already loaded the extension GL functions we use (e.g glBeginQuery) +#include "../../lib/irrlicht/source/Irrlicht/COpenGLDriver.h" + +#endif diff --git a/src/graphics/hardware_skinning.cpp b/src/graphics/hardware_skinning.cpp index 2d9d708c0..976404ffb 100644 --- a/src/graphics/hardware_skinning.cpp +++ b/src/graphics/hardware_skinning.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// Copyright (C) 2004-2013 Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/hardware_skinning.hpp b/src/graphics/hardware_skinning.hpp index a572152f3..6f4144210 100644 --- a/src/graphics/hardware_skinning.hpp +++ b/src/graphics/hardware_skinning.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 the SuperTuxKart team +// Copyright (C) 2011-2013 the SuperTuxKart team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/hit_effect.hpp b/src/graphics/hit_effect.hpp index 903808b84..e2ec5966a 100644 --- a/src/graphics/hit_effect.hpp +++ b/src/graphics/hit_effect.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/hit_sfx.cpp b/src/graphics/hit_sfx.cpp index 4a6541931..815ae624a 100644 --- a/src/graphics/hit_sfx.cpp +++ b/src/graphics/hit_sfx.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/hit_sfx.hpp b/src/graphics/hit_sfx.hpp index 2d19bf492..d7f7e2e5a 100644 --- a/src/graphics/hit_sfx.hpp +++ b/src/graphics/hit_sfx.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 5f60c3655..e9cccd2ee 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,13 +19,23 @@ #include "graphics/irr_driver.hpp" #include "config/user_config.hpp" +#include "graphics/callbacks.hpp" #include "graphics/camera.hpp" +#include "graphics/glwrap.hpp" #include "graphics/hardware_skinning.hpp" +#include "graphics/lens_flare.hpp" +#include "graphics/light.hpp" #include "graphics/material_manager.hpp" #include "graphics/particle_kind_manager.hpp" #include "graphics/per_camera_node.hpp" #include "graphics/post_processing.hpp" #include "graphics/referee.hpp" +#include "graphics/shaders.hpp" +#include "graphics/shadow_importance.hpp" +#include "graphics/sun.hpp" +#include "graphics/rtts.hpp" +#include "graphics/water.hpp" +#include "graphics/wind.hpp" #include "guiengine/engine.hpp" #include "guiengine/modaldialog.hpp" #include "guiengine/scalable_font.hpp" @@ -47,6 +57,7 @@ #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/profiler.hpp" +#include "utils/vs.hpp" #include @@ -62,19 +73,12 @@ using namespace irr; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - #ifdef WIN32 #include #endif #if defined(__linux__) && !defined(ANDROID) -namespace X11 -{ - #include - #include -} +#include +#include #endif /** singleton */ @@ -96,6 +100,11 @@ IrrDriver::IrrDriver() m_resolution_changing = RES_CHANGE_NONE; m_device = createDevice(video::EDT_NULL); m_request_screenshot = false; + m_shaders = NULL; + m_rtts = NULL; + m_wind = new Wind(); + m_mipviz = m_wireframe = m_normals = m_ssaoviz = \ + m_lightviz = m_shadowviz = m_distortviz = 0; } // IrrDriver // ---------------------------------------------------------------------------- @@ -117,6 +126,9 @@ IrrDriver::~IrrDriver() m_device->drop(); m_device = NULL; m_modes.clear(); + + delete m_shaders; + delete m_wind; } // ~IrrDriver // ---------------------------------------------------------------------------- @@ -124,7 +136,7 @@ IrrDriver::~IrrDriver() */ void IrrDriver::reset() { - m_post_processing->reset(); + if (m_glsl) m_post_processing->reset(); } // reset // ---------------------------------------------------------------------------- @@ -135,22 +147,22 @@ Returns the parent window of "window" (i.e. the ancestor of window that is a direct child of the root, or window itself if it is a direct child). If window is the root window, returns window. */ -X11::Window get_toplevel_parent(X11::Display* display, X11::Window window) +Window get_toplevel_parent(Display* display, Window window) { - X11::Window parent; - X11::Window root; - X11::Window * children; + Window parent; + Window root; + Window * children; unsigned int num_children; while (true) { - if (0 == X11::XQueryTree(display, window, &root, + if (0 == XQueryTree(display, window, &root, &parent, &children, &num_children)) { Log::fatal("irr_driver", "XQueryTree error\n"); } if (children) { //must test for null - X11::XFree(children); + XFree(children); } if (window == root || parent == root) { return window; @@ -201,7 +213,6 @@ void IrrDriver::updateConfigIfRelevant() Log::warn("irr_driver", "Could not retrieve window location\n"); } #elif defined(__linux__) && !defined(ANDROID) - using namespace X11; const video::SExposedVideoData& videoData = m_device->getVideoDriver()->getExposedVideoData(); Display* display = (Display*)videoData.OpenGLLinux.X11Display; @@ -265,8 +276,8 @@ void IrrDriver::initDevice() if(m_modes.size()==0) { createListOfVideoModes(); - // The debug name is only set if irrlicht is compiled in debug - // mode. So we use this to print a warning to the user. + // The debug name is only set if irrlicht is compiled in debug + // mode. So we use this to print a warning to the user. if(m_device->getDebugName()) { Log::warn("irr_driver", @@ -326,6 +337,8 @@ void IrrDriver::initDevice() params.WindowSize = core::dimension2du(UserConfigParams::m_width, UserConfigParams::m_height); + + /* switch ((int)UserConfigParams::m_antialiasing) { case 0: @@ -345,9 +358,12 @@ void IrrDriver::initDevice() "anti-alias setting : %i\n", (int)UserConfigParams::m_antialiasing); } + */ + m_device = createDeviceEx(params); + if(m_device) + break; } // for bits=32, 24, 16 - m_device = createDeviceEx(params); // if still no device, try with a standard 800x600 window size, maybe // size is the problem @@ -382,7 +398,8 @@ void IrrDriver::initDevice() m_gui_env = m_device->getGUIEnvironment(); m_video_driver = m_device->getVideoDriver(); m_glsl = m_video_driver->queryFeature(video::EVDF_ARB_GLSL) && - m_video_driver->queryFeature(video::EVDF_TEXTURE_NPOT); + m_video_driver->queryFeature(video::EVDF_TEXTURE_NPOT) && + UserConfigParams::m_pixel_shaders; // This remaps the window, so it has to be done before the clear to avoid flicker m_device->setResizable(false); @@ -394,10 +411,48 @@ void IrrDriver::initDevice() if (m_glsl) { Log::info("irr_driver", "GLSL supported."); + + // Order matters, create RTTs as soon as possible, as they are the largest blocks. + m_rtts = new RTT(); + m_shaders = new Shaders(); + m_shadow_importance = new ShadowImportance(); + + m_mrt.clear(); + m_mrt.reallocate(3); + m_mrt.push_back(m_rtts->getRTT(RTT_COLOR)); + m_mrt.push_back(m_rtts->getRTT(RTT_NORMAL)); + m_mrt.push_back(m_rtts->getRTT(RTT_DEPTH)); + + irr::video::COpenGLDriver* gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver(); + gl_driver->extGlGenQueries(1, &m_lensflare_query); + + scene::IMesh * const sphere = m_scene_manager->getGeometryCreator()->createSphereMesh(1, 16, 16); + m_sun_interposer = m_scene_manager->addMeshSceneNode(sphere); + m_sun_interposer->grab(); + m_sun_interposer->setParent(NULL); + m_sun_interposer->setScale(core::vector3df(20)); + + m_sun_interposer->getMaterial(0).Lighting = false; + m_sun_interposer->getMaterial(0).ColorMask = video::ECP_NONE; + m_sun_interposer->getMaterial(0).ZWriteEnable = false; + m_sun_interposer->getMaterial(0).MaterialType = m_shaders->getShader(ES_PASSFAR); + + sphere->drop(); + + m_lensflare = new scene::CLensFlareSceneNode(NULL, m_scene_manager, -1); + video::ITexture * const tex = + m_video_driver->getTexture((file_manager->getTextureDir() + "lensflare.png").c_str()); + if (!tex) Log::fatal("irr_driver", "Cannot find lens flare texture"); + m_lensflare->setMaterialTexture(0, tex); + m_lensflare->setAutomaticCulling(scene::EAC_OFF); + + m_suncam = m_scene_manager->addCameraSceneNode(0, vector3df(0), vector3df(0), -1, false); + m_suncam->grab(); + m_suncam->setParent(NULL); } else { - Log::warn("irr_driver", "Too old GPU; using the fixed pipeline."); + Log::warn("irr_driver", "Using the fixed pipeline (old GPU, or shaders disabled in options)"); } // Only change video driver settings if we are showing graphics @@ -405,7 +460,6 @@ void IrrDriver::initDevice() { #if defined(__linux__) && !defined(ANDROID) // Set class hints on Linux, used by Window Managers. - using namespace X11; const video::SExposedVideoData& videoData = m_video_driver ->getExposedVideoData(); XClassHint* classhint = XAllocClassHint(); @@ -421,11 +475,6 @@ void IrrDriver::initDevice() ->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true); m_device->getVideoDriver() ->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_QUALITY, true); - if (!UserConfigParams::m_fbo) - { - m_device->getVideoDriver() - ->disableFeature(video::EVDF_FRAMEBUFFER_OBJECT); - } // Force creation of mipmaps even if the mipmaps flag in a b3d file // does not set the 'enable mipmap' flag. @@ -524,7 +573,6 @@ bool IrrDriver::moveWindow(const int x, const int y) return false; } #elif defined(__linux__) && !defined(ANDROID) - using namespace X11; const video::SExposedVideoData& videoData = m_video_driver->getExposedVideoData(); // TODO: Actually handle possible failure XMoveWindow((Display*)videoData.OpenGLLinux.X11Display, @@ -769,6 +817,7 @@ void IrrDriver::setAllMaterialFlags(scene::IMesh *mesh) const * \param wave_length Lenght of a water wave. */ scene::ISceneNode* IrrDriver::addWaterNode(scene::IMesh *mesh, + scene::IMesh **welded, float wave_height, float wave_speed, float wave_length) @@ -776,11 +825,24 @@ scene::ISceneNode* IrrDriver::addWaterNode(scene::IMesh *mesh, mesh->setMaterialFlag(video::EMF_GOURAUD_SHADING, true); scene::IMesh* welded_mesh = m_scene_manager->getMeshManipulator() ->createMeshWelded(mesh); - scene::ISceneNode* out = m_scene_manager->addWaterSurfaceSceneNode(welded_mesh, + scene::ISceneNode* out = NULL; + + if (!m_glsl) + { + out = m_scene_manager->addWaterSurfaceSceneNode(welded_mesh, wave_height, wave_speed, wave_length); + } else + { + out = new WaterNode(m_scene_manager, welded_mesh, wave_height, wave_speed, + wave_length); + } + out->getMaterial(0).setFlag(video::EMF_GOURAUD_SHADING, true); welded_mesh->drop(); // The scene node keeps a reference + + *welded = welded_mesh; + return out; } // addWaterNode @@ -839,11 +901,22 @@ PerCameraNode *IrrDriver::addPerCameraMesh(scene::IMesh* mesh, scene::ICameraSceneNode* camera, scene::ISceneNode *parent) { + scene::ISceneNode *node = m_scene_manager->addMeshSceneNode(mesh); + return new PerCameraNode((parent ? parent : m_scene_manager->getRootSceneNode()), - m_scene_manager, -1, camera, mesh); + m_scene_manager, -1, camera, node); } // addMesh +PerCameraNode *IrrDriver::addPerCameraNode(scene::ISceneNode* node, + scene::ICameraSceneNode* camera, + scene::ISceneNode *parent) +{ + return new PerCameraNode((parent ? parent + : m_scene_manager->getRootSceneNode()), + m_scene_manager, -1, camera, node); +} // addNode + // ---------------------------------------------------------------------------- /** Adds a billboard node to scene. @@ -856,8 +929,8 @@ scene::ISceneNode *IrrDriver::addBillboard(const core::dimension2d< f32 > size, m_scene_manager->addBillboardSceneNode(parent, size); assert(node->getMaterialCount() > 0); node->setMaterialTexture(0, texture); - if(alphaTesting) - node->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF); + if(alphaTesting) + node->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF); return node; } // addMesh @@ -1067,6 +1140,32 @@ void IrrDriver::removeCameraSceneNode(scene::ICameraSceneNode *camera) camera->remove(); } // removeCameraSceneNode +// ---------------------------------------------------------------------------- +/** Sets an error message to be displayed when a texture is not found. This + * error message is shown before the "Texture '%s' not found" message. It can + * be used to supply additional details like what kart is currently being + * loaded. + * \param error Error message, potentially with a '%' which will be replaced + * with detail. + * \param detail String to replace a '%' in the error message. + */ +void IrrDriver::setTextureErrorMessage(const std::string &error, + const std::string &detail) +{ + if(detail=="") + m_texture_error_message = error; + else + m_texture_error_message = StringUtils::insertValues(error, detail); +} // setTextureErrorMessage + +// ---------------------------------------------------------------------------- +/** Disables the texture error message again. + */ +void IrrDriver::unsetTextureErrorMessage() +{ + m_texture_error_message = ""; +} // unsetTextureErrorMessage + // ---------------------------------------------------------------------------- /** Loads a texture from a file and returns the texture object. * \param filename File name of the texture to load. @@ -1096,7 +1195,7 @@ video::ITexture *IrrDriver::getTexture(const std::string &filename, // PNGs are non premul, but some are used for premul tasks, so convert // http://home.comcast.net/~tom_forsyth/blog.wiki.html#[[Premultiplied%20alpha]] // FIXME check param, not name - if(is_premul && + if(img && is_premul && StringUtils::hasSuffix(filename.c_str(), ".png") && (img->getColorFormat() == video::ECF_A8R8G8B8) && img->lock()) @@ -1119,7 +1218,7 @@ video::ITexture *IrrDriver::getTexture(const std::string &filename, } // if png and ColorFOrmat and lock // Other formats can be premul, but the tasks can be non premul // So divide to get the separate RGBA (only possible if alpha!=0) - else if(is_prediv && + else if(img && is_prediv && (img->getColorFormat() == video::ECF_A8R8G8B8) && img->lock()) { @@ -1148,9 +1247,12 @@ video::ITexture *IrrDriver::getTexture(const std::string &filename, if (complain_if_not_found && out == NULL) { - Log::error("irr_driver", "Texture '%s' not found; Put a breakpoint " - "at line %s:%i to debug!\n", - filename.c_str(), __FILE__, __LINE__); + + if(m_texture_error_message.size()>0) + { + Log::error("irr_driver", m_texture_error_message.c_str()); + } + Log::error("irr_driver", "Texture '%s' not found.", filename.c_str()); } return out; @@ -1320,7 +1422,7 @@ void IrrDriver::displayFPS() else { sprintf(buffer, "FPS: %i/%i/%i - %i KTris", min, fps, max, - (int)round(kilotris)); + (int)roundf(kilotris)); } core::stringw fpsString = buffer; @@ -1581,6 +1683,11 @@ void IrrDriver::update(float dt) return; } + // If we quit via the menu the m_device->run() does not return true. + // To avoid any other calls, we return here. + if(main_loop->isAborted()) + return; + // If the resolution should be switched, do it now. This will delete the // old device and create a new one. if (m_resolution_changing!=RES_CHANGE_NONE) @@ -1591,6 +1698,8 @@ void IrrDriver::update(float dt) m_resolution_changing = RES_CHANGE_NONE; } + m_wind->update(); + World *world = World::getWorld(); // Handle cut scenes (which do not have any karts in it) @@ -1615,97 +1724,24 @@ void IrrDriver::update(float dt) m_video_driver->endScene(); return; } - - const bool inRace = world!=NULL; - - if (inRace) + else if (!world) { - // Start the RTT for post-processing. - // We do this before beginScene() because we want to capture the glClear() - // because of tracks that do not have skyboxes (generally add-on tracks) - m_post_processing->beginCapture(); - } - m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, - world ? world->getClearColor() - : video::SColor(255,100,101,140)); + video::SColor(255,100,101,140)); - if (inRace) - { - irr_driver->getVideoDriver()->enableMaterial2D(); - - RaceGUIBase *rg = world->getRaceGUI(); - if (rg) rg->update(dt); - - - for(unsigned int i=0; iactivate(); - rg->preRenderCallback(camera); // adjusts start referee - m_scene_manager->drawAll(); - - PROFILER_POP_CPU_MARKER(); - - // Note that drawAll must be called before rendering - // the bullet debug view, since otherwise the camera - // is not set up properly. This is only used for - // the bullet debug view. - if (UserConfigParams::m_artist_debug_mode) - World::getWorld()->getPhysics()->draw(); - } // for igetNumKarts() - - // Stop capturing for the post-processing - m_post_processing->endCapture(); - - // Render the post-processed scene - m_post_processing->render(); - - // Set the viewport back to the full screen for race gui - m_video_driver->setViewPort(core::recti(0, 0, - UserConfigParams::m_width, - UserConfigParams::m_height)); - - for(unsigned int i=0; irenderPlayerView(camera, dt); - - PROFILER_POP_CPU_MARKER(); - } // for iendScene(); + return; } - -#ifdef DEBUG - drawDebugMeshes(); -#endif - - m_video_driver->endScene(); + if (m_glsl) + renderGLSL(dt); + else + renderFixed(dt); if (m_request_screenshot) doScreenShot(); - getPostProcessing()->update(dt); - // Enable this next print statement to get render information printed // E.g. number of triangles rendered, culled etc. The stats is only // printed while the race is running and not while the in-game menu @@ -1739,7 +1775,7 @@ bool IrrDriver::OnEvent(const irr::SEvent &event) // Ignore 'normal' messages if (event.LogEvent.Level > 1) { - printf("[IrrDriver Temp Logger] Level %d: %s\n", + Log::warn("[IrrDriver Temp Logger]", "Level %d: %s\n", event.LogEvent.Level,event.LogEvent.Text); } return true; @@ -1755,7 +1791,7 @@ bool IrrDriver::OnEvent(const irr::SEvent &event) bool IrrDriver::supportsSplatting() { - return UserConfigParams::m_pixel_shaders && m_glsl; + return m_glsl; } // ---------------------------------------------------------------------------- @@ -1941,6 +1977,11 @@ video::ITexture* IrrDriver::RTTProvider::renderToTexture(float angle, if (angle != -1 && m_rtt_main_node != NULL) m_rtt_main_node->setRotation( core::vector3df(0, angle, 0) ); + video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial(); + overridemat.EnablePasses = scene::ESNRP_SOLID; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE; + overridemat.Material.MaterialType = video::EMT_SOLID; + if (m_rtt_main_node == NULL) { irr_driver->getSceneManager()->drawAll(); @@ -1954,6 +1995,129 @@ video::ITexture* IrrDriver::RTTProvider::renderToTexture(float angle, m_light->setVisible(false); } + overridemat.EnablePasses = 0; + m_video_driver->setRenderTarget(0, false, false); return m_render_target_texture; } + +void IrrDriver::applyObjectPassShader(scene::ISceneNode * const node, bool rimlit) +{ + if (!m_glsl) + return; + + // Don't override sky + if (node->getType() == scene::ESNT_SKY_DOME || + node->getType() == scene::ESNT_SKY_BOX) + return; + + const u32 mcount = node->getMaterialCount(); + u32 i; + const video::E_MATERIAL_TYPE ref = rimlit ? m_shaders->getShader(ES_OBJECTPASS_RIMLIT): + m_shaders->getShader(ES_OBJECTPASS_REF); + const video::E_MATERIAL_TYPE pass = rimlit ? m_shaders->getShader(ES_OBJECTPASS_RIMLIT): + m_shaders->getShader(ES_OBJECTPASS); + + const video::E_MATERIAL_TYPE origref = m_shaders->getShader(ES_OBJECTPASS_REF); + const video::E_MATERIAL_TYPE origpass = m_shaders->getShader(ES_OBJECTPASS); + + bool viamb = false; + scene::IMesh *mesh = NULL; + if (node->getType() == scene::ESNT_ANIMATED_MESH) + { + viamb = ((scene::IAnimatedMeshSceneNode *) node)->isReadOnlyMaterials(); + mesh = ((scene::IAnimatedMeshSceneNode *) node)->getMesh(); + } + else if (node->getType() == scene::ESNT_MESH) + { + viamb = ((scene::IMeshSceneNode *) node)->isReadOnlyMaterials(); + mesh = ((scene::IMeshSceneNode *) node)->getMesh(); + } + + for (i = 0; i < mcount; i++) + { + video::SMaterial &nodemat = node->getMaterial(i); + video::SMaterial &mbmat = mesh ? mesh->getMeshBuffer(i)->getMaterial() : nodemat; + video::SMaterial *mat = &nodemat; + + if (viamb) + mat = &mbmat; + + if (mat->MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF || + mat->MaterialType == origref) + mat->MaterialType = ref; + else if (mat->MaterialType == video::EMT_SOLID || + mat->MaterialType == origpass || + (mat->MaterialType >= video::EMT_LIGHTMAP && + mat->MaterialType <= video::EMT_LIGHTMAP_LIGHTING_M4)) + mat->MaterialType = pass; + } + + + core::list kids = node->getChildren(); + scene::ISceneNodeList::Iterator it = kids.begin(); + for (; it != kids.end(); ++it) + { + applyObjectPassShader(*it, rimlit); + } +} + +void IrrDriver::applyObjectPassShader() +{ + if (!m_glsl) + return; + + applyObjectPassShader(m_scene_manager->getRootSceneNode()); +} + +scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos, float radius, + float r, float g, float b, bool sun) +{ + if (m_glsl) + { + LightNode *light = NULL; + + if (!sun) + light = new LightNode(m_scene_manager, radius, r, g, b); + else + light = new SunNode(m_scene_manager, r, g, b); + + light->grab(); + light->setParent(NULL); + + light->setPosition(pos); + light->updateAbsolutePosition(); + + m_lights.push_back(light); + + if (sun) { + m_sun_interposer->setPosition(pos); + m_sun_interposer->updateAbsolutePosition(); + + m_lensflare->setPosition(pos); + m_lensflare->updateAbsolutePosition(); + + m_suncam->setPosition(pos); + m_suncam->updateAbsolutePosition(); + + ((WaterShaderProvider *) m_shaders->m_callbacks[ES_WATER])->setSunPosition(pos); + } + + return light; + } else + { + return m_scene_manager->addLightSceneNode(NULL, pos, video::SColorf(r, g, b), radius); + } +} + +void IrrDriver::clearLights() +{ + u32 i; + const u32 max = m_lights.size(); + for (i = 0; i < max; i++) + { + m_lights[i]->drop(); + } + + m_lights.clear(); +} diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index c43ca9cf4..ad28c204a 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -39,11 +39,15 @@ namespace irr { namespace scene { class ISceneManager; class IMesh; class IAnimatedMeshSceneNode; class IAnimatedMesh; - class IMeshSceneNode; class IParticleSystemSceneNode; class ICameraSceneNode; class ILightSceneNode; } + class IMeshSceneNode; class IParticleSystemSceneNode; class ICameraSceneNode; class ILightSceneNode; + class CLensFlareSceneNode; } namespace gui { class IGUIEnvironment; class IGUIFont; } } using namespace irr; +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" +#include "graphics/wind.hpp" #include "utils/aligned_array.hpp" #include "utils/no_copy.hpp" #include "utils/ptr_vector.hpp" @@ -53,6 +57,8 @@ class AbstractKart; class Camera; class PerCameraNode; class PostProcessing; +class LightNode; +class ShadowImportance; /** * \brief class that creates the irrLicht device and offers higher-level @@ -74,6 +80,21 @@ private: gui::IGUIFont *m_race_font; /** Post-processing. */ PostProcessing *m_post_processing; + /** Shaders. */ + Shaders *m_shaders; + /** Wind. */ + Wind *m_wind; + /** RTTs. */ + RTT *m_rtts; + /** Shadow importance. */ + ShadowImportance *m_shadow_importance; + + /** Additional details to be shown in case that a texture is not found. + * This is used to specify details like: "while loading kart '...'" */ + std::string m_texture_error_message; + + /** The main MRT setup. */ + core::array m_mrt; /** Flag to indicate if a resolution change is pending (which will be * acted upon in the next update). None means no change, yes means @@ -95,6 +116,12 @@ public: int getWidth() const {return m_width; } int getHeight() const {return m_height; } }; // VideoMode + + struct BloomData { + scene::ISceneNode * node; + float power; + }; + private: std::vector m_modes; @@ -112,6 +139,34 @@ private: bool m_request_screenshot; + bool m_wireframe; + bool m_mipviz; + bool m_normals; + bool m_ssaoviz; + bool m_shadowviz; + bool m_lightviz; + bool m_distortviz; + u32 m_renderpass; + u32 m_lensflare_query; + scene::IMeshSceneNode *m_sun_interposer; + scene::CLensFlareSceneNode *m_lensflare; + scene::ICameraSceneNode *m_suncam; + + struct GlowData { + scene::ISceneNode * node; + float r, g, b; + }; + + std::vector m_glowing; + + std::vector m_lights; + + std::vector m_forcedbloom; + + std::vector m_displacing; + + std::vector m_background; + #ifdef DEBUG /** Used to visualise skeletons. */ std::vector m_debug_meshes; @@ -122,6 +177,9 @@ private: irr::scene::ISkinnedMesh* mesh, int id); #endif + void renderFixed(float dt); + void renderGLSL(float dt); + void doScreenShot(); public: IrrDriver(); @@ -147,7 +205,8 @@ public: bool create_one_quad=false); scene::IMesh *createTexturedQuadMesh(const video::SMaterial *material, const double w, const double h); - scene::ISceneNode *addWaterNode(scene::IMesh *mesh, float wave_height, + scene::ISceneNode *addWaterNode(scene::IMesh *mesh, scene::IMesh **welded, + float wave_height, float wave_speed, float wave_length); scene::IMeshSceneNode*addOctTree(scene::IMesh *mesh); scene::IMeshSceneNode*addSphere(float radius, @@ -157,6 +216,9 @@ public: PerCameraNode *addPerCameraMesh(scene::IMesh* mesh, scene::ICameraSceneNode* node, scene::ISceneNode *parent = NULL); + PerCameraNode *addPerCameraNode(scene::ISceneNode* node, + scene::ICameraSceneNode* cam, + scene::ISceneNode *parent = NULL); scene::ISceneNode *addBillboard(const core::dimension2d< f32 > size, video::ITexture *texture, scene::ISceneNode* parent=NULL, bool alphaTesting = false); @@ -193,6 +255,9 @@ public: void printRenderStats(); bool supportsSplatting(); void requestScreenshot(); + void setTextureErrorMessage(const std::string &error, + const std::string &detail=""); + void unsetTextureErrorMessage(); void draw2dTriangle(const core::vector2df &a, const core::vector2df &b, const core::vector2df &c, @@ -203,6 +268,55 @@ public: + // ------------------------------------------------------------------------ + /** Convenience function that loads a texture with default parameters + * but includes an error message. + * \param filename File name of the texture to load. + * \param error Error message, potentially with a '%' which will be replaced + * with detail. + * \param detail String to replace a '%' in the error message. + */ + video::ITexture* getTexture(const std::string &filename, + const std::string &error_message, + const std::string &detail="") + { + setTextureErrorMessage(error_message, detail); + video::ITexture *tex = getTexture(filename); + unsetTextureErrorMessage(); + return tex; + } // getTexture + + // ------------------------------------------------------------------------ + /** Convenience function that loads a texture with default parameters + * but includes an error message. + * \param filename File name of the texture to load. + * \param error Error message, potentially with a '%' which will be replaced + * with detail. + * \param detail String to replace a '%' in the error message. + */ + video::ITexture* getTexture(const std::string &filename, + char *error_message, + char *detail=NULL) + { + if(!detail) + return getTexture(filename, std::string(error_message), + std::string("")); + + return getTexture(filename, std::string(error_message), + std::string(detail)); + } // getTexture + + // ------------------------------------------------------------------------ + /** Returns the currently defined texture error message, which is used + * by event_handler.cpp to print additional info about irrlicht + * internal errors or warnings. If no error message is currently + * defined, the error message is "". + */ + const std::string &getTextureErrorMessage() + { + return m_texture_error_message; + } // getTextureErrorMessage + // ------------------------------------------------------------------------ /** Returns a list of all video modes supports by the graphics card. */ const std::vector& getVideoModes() const { return m_modes; } @@ -230,9 +344,89 @@ public: /** Returns a pointer to the post processing object. */ inline PostProcessing* getPostProcessing() {return m_post_processing;} // ------------------------------------------------------------------------ - + inline core::vector3df getWind() {return m_wind->getWind();} + // ------------------------------------------------------------------------ + inline video::E_MATERIAL_TYPE getShader(const ShaderType num) {return m_shaders->getShader(num);} + // ------------------------------------------------------------------------ + inline video::IShaderConstantSetCallBack* getCallback(const ShaderType num) + { + return (m_shaders == NULL ? NULL : m_shaders->m_callbacks[num]); + } + // ------------------------------------------------------------------------ + inline video::ITexture* getRTT(TypeRTT which) {return m_rtts->getRTT(which);} + // ------------------------------------------------------------------------ inline bool isGLSL() const { return m_glsl; } // ------------------------------------------------------------------------ + void toggleWireframe() { m_wireframe ^= 1; } + // ------------------------------------------------------------------------ + void toggleMipVisualization() { m_mipviz ^= 1; } + // ------------------------------------------------------------------------ + void toggleNormals() { m_normals ^= 1; } + // ------------------------------------------------------------------------ + bool getNormals() { return m_normals; } + // ------------------------------------------------------------------------ + void toggleSSAOViz() { m_ssaoviz ^= 1; } + // ------------------------------------------------------------------------ + void toggleLightViz() { m_lightviz ^= 1; } + // ------------------------------------------------------------------------ + bool getSSAOViz() { return m_ssaoviz; } + // ------------------------------------------------------------------------ + void toggleShadowViz() { m_shadowviz ^= 1; } + // ------------------------------------------------------------------------ + bool getShadowViz() { return m_shadowviz; } + // ------------------------------------------------------------------------ + void toggleDistortViz() { m_distortviz ^= 1; } + // ------------------------------------------------------------------------ + bool getDistortViz() { return m_distortviz; } + // ------------------------------------------------------------------------ + u32 getRenderPass() { return m_renderpass; } + // ------------------------------------------------------------------------ + void addGlowingNode(scene::ISceneNode *n, float r = 1.0f, float g = 1.0f, float b = 1.0f) + { + GlowData dat; + dat.node = n; + dat.r = r; + dat.g = g; + dat.b = b; + + m_glowing.push_back(dat); + } + // ------------------------------------------------------------------------ + void clearGlowingNodes() { m_glowing.clear(); } + // ------------------------------------------------------------------------ + void addForcedBloomNode(scene::ISceneNode *n, float power = 1) + { + BloomData dat; + dat.node = n; + dat.power = power; + + m_forcedbloom.push_back(dat); + } + // ------------------------------------------------------------------------ + void clearForcedBloom() { m_forcedbloom.clear(); } + // ------------------------------------------------------------------------ + const std::vector &getForcedBloom() const { return m_forcedbloom; } + // ------------------------------------------------------------------------ + void clearDisplacingNodes() { m_displacing.clear(); } + // ------------------------------------------------------------------------ + const std::vector &getDisplacingNodes() const { return m_displacing; } + // ------------------------------------------------------------------------ + void addDisplacingNode(scene::ISceneNode * const n) { m_displacing.push_back(n); } + // ------------------------------------------------------------------------ + void clearBackgroundNodes() { m_background.clear(); } + // ------------------------------------------------------------------------ + void addBackgroundNode(scene::ISceneNode * const n) { m_background.push_back(n); } + // ------------------------------------------------------------------------ + void applyObjectPassShader(); + void applyObjectPassShader(scene::ISceneNode * const node, bool rimlit = false); + // ------------------------------------------------------------------------ + scene::ISceneNode *addLight(const core::vector3df &pos, float radius = 1.0f, float r = 1.0f, + float g = 1.0f, float b = 1.0f, bool sun = false); + // ------------------------------------------------------------------------ + void clearLights(); + // ------------------------------------------------------------------------ + scene::IMeshSceneNode *getSunInterposer() { return m_sun_interposer; } + #ifdef DEBUG /** Removes debug meshes. */ void clearDebugMesh() { m_debug_meshes.clear(); } diff --git a/src/graphics/large_mesh_buffer.hpp b/src/graphics/large_mesh_buffer.hpp new file mode 100644 index 000000000..84c77fd17 --- /dev/null +++ b/src/graphics/large_mesh_buffer.hpp @@ -0,0 +1,58 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __LARGE_MESH_BUFFER_H_INCLUDED__ +#define __LARGE_MESH_BUFFER_H_INCLUDED__ + +#include "irrArray.h" +#include "IMeshBuffer.h" +#include "CMeshBuffer.h" + +namespace irr +{ +namespace scene +{ + //! A SMeshBuffer with 32-bit indices + class LargeMeshBuffer : public SMeshBuffer + { + public: + //! Get type of index data which is stored in this meshbuffer. + /** \return Index type of this buffer. */ + virtual video::E_INDEX_TYPE getIndexType() const + { + return video::EIT_32BIT; + } + + //! Get pointer to indices + /** \return Pointer to indices. */ + virtual const u16* getIndices() const + { + return (u16 *) Indices.const_pointer(); + } + + + //! Get pointer to indices + /** \return Pointer to indices. */ + virtual u16* getIndices() + { + return (u16 *) Indices.pointer(); + } + + + //! Get number of indices + /** \return Number of indices. */ + virtual u32 getIndexCount() const + { + return Indices.size(); + } + + //! Indices into the vertices of this buffer. + core::array Indices; + }; +} // end namespace scene +} // end namespace irr + +#endif + + diff --git a/src/graphics/lens_flare.cpp b/src/graphics/lens_flare.cpp new file mode 100644 index 000000000..eebc27d16 --- /dev/null +++ b/src/graphics/lens_flare.cpp @@ -0,0 +1,264 @@ +/* TODO: copyright */ + +#include "graphics/lens_flare.hpp" +#include "IVideoDriver.h" +#include "ISceneManager.h" +#include "ISceneCollisionManager.h" + +namespace irr +{ +namespace scene +{ + +CLensFlareSceneNode::CLensFlareSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id) : + scene::ISceneNode(parent, mgr, id) +{ + #ifdef _DEBUG + setDebugName("CLensFlareSceneNode"); + #endif + + // set the bounding box + BBox.MaxEdge.set(0,0,0); + BBox.MinEdge.set(0,0,0); + + // set the initial Strength + Strength = 1.0f; + + // setup the vertices + Vertices[0] = video::S3DVertex(-1.f, -1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 0.f, 1.f); + Vertices[1] = video::S3DVertex(-1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 0.f, 0.f); + Vertices[2] = video::S3DVertex( 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 1.f, 0.f); + Vertices[3] = video::S3DVertex( 1.f, -1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 1.f, 1.f); + + // setup the indices + Indices[0] = 0; + Indices[1] = 1; + Indices[2] = 2; + Indices[3] = 2; + Indices[4] = 3; + Indices[5] = 0; + + // set the default material properties + Material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; + Material.Lighting = false; + Material.ZBuffer = video::ECFN_NEVER; + + FlareData.reallocate(30); + + // prepare the flare data array + // circles, halos and ring behind the sun + FlareData.push_back(SFlareData(EFT_CIRCLE, 0.5f, 0.12f, video::SColor(120, 60, 180, 35))); + FlareData.push_back(SFlareData(EFT_HALO, 0.45f, 0.4f, video::SColor(200, 100, 200, 60))); + FlareData.push_back(SFlareData(EFT_CIRCLE, 0.4f, 0.17f, video::SColor(240, 120, 220, 40))); + FlareData.push_back(SFlareData(EFT_CIRCLE, 0.2f, 0.35f, video::SColor(175, 175, 255, 20))); + FlareData.push_back(SFlareData(EFT_RING, 0.15f, 0.2f, video::SColor(120, 60, 255, 100))); + + // sun and glow effect at sun position + FlareData.push_back(SFlareData(EFT_SUN, 0.0f, 0.75f, video::SColor(255, 255, 255, 255))); +// FlareData.push_back(SFlareData(EFT_STREAKS, 0.0f, 2.9f, video::SColor(255, 255, 255, 255))); + FlareData.push_back(SFlareData(EFT_GLOW, 0.0f, 3.5f, video::SColor(255, 255, 255, 255))); +// FlareData.push_back(SFlareData(EFT_RING, 0.0f, 1.5f, video::SColor(120, 120, 120, 150))); + + // some lenses, halos and circles + FlareData.push_back(SFlareData(EFT_LENS, -0.15f, 0.15f, video::SColor(255, 60, 60, 90))); + FlareData.push_back(SFlareData(EFT_HALO, -0.3f, 0.3f, video::SColor(120, 60, 255, 180))); + FlareData.push_back(SFlareData(EFT_HALO, -0.4f, 0.2f, video::SColor(220, 80, 80, 98))); + FlareData.push_back(SFlareData(EFT_CIRCLE, -0.45f, 0.1f, video::SColor(220, 80, 80, 85))); + FlareData.push_back(SFlareData(EFT_RING, -0.42f, 0.3f, video::SColor(180, 60, 255, 110))); + + // some small lenses, halos and rings + FlareData.push_back(SFlareData(EFT_LENS, -0.55f, 0.2f, video::SColor(255, 60, 60, 130))); + FlareData.push_back(SFlareData(EFT_HALO, -0.6f, 0.3f, video::SColor(120, 60, 255, 80))); + FlareData.push_back(SFlareData(EFT_LENS, -0.7f, 0.2f, video::SColor(200, 60, 60, 130))); + FlareData.push_back(SFlareData(EFT_LENS, -0.71f, 0.2f, video::SColor(200, 60, 130, 60))); + FlareData.push_back(SFlareData(EFT_LENS, -0.72f, 0.2f, video::SColor(200, 130, 130, 60))); + FlareData.push_back(SFlareData(EFT_LENS, -0.74f, 0.2f, video::SColor(200, 130, 60, 60))); + + // some polyons, lenses and circle + FlareData.push_back(SFlareData(scene::EFT_POLY, -0.79f, 0.2f, video::SColor(200, 60, 130, 60))); + FlareData.push_back(SFlareData(scene::EFT_POLY, -0.86f, 0.3f, video::SColor(200, 130, 130, 60))); + FlareData.push_back(SFlareData(scene::EFT_LENS, -0.87f, 0.3f, video::SColor(180,255,192,178))); + FlareData.push_back(SFlareData(scene::EFT_CIRCLE, -0.9f, 0.1f, video::SColor(200, 60, 60, 130))); + FlareData.push_back(SFlareData(scene::EFT_POLY, -0.93f, 0.4f, video::SColor(200, 130, 60, 60))); + + // finally som polygons + FlareData.push_back(SFlareData(EFT_POLY, -0.95f, 0.6f, video::SColor(120, 60, 255, 120))); + FlareData.push_back(SFlareData(EFT_POLY, -1.0f, 0.15f, video::SColor(120, 20, 255, 85))); +} + +CLensFlareSceneNode::~CLensFlareSceneNode() +{ +} + +u32 CLensFlareSceneNode::getMaterialCount() const +{ + // return the material count (always one in our case) + return 1; +} + +video::SMaterial& CLensFlareSceneNode::getMaterial(u32 i) +{ + // return the material + return Material; +} + +core::array& CLensFlareSceneNode::getFlareData() +{ + // return the flare data array + return FlareData; +} + +void CLensFlareSceneNode::OnRegisterSceneNode() +{ + // if node is visible and Strength is greater than 0 register it for the ESNRP_TRANSPARENT_EFFECT pass + if(IsVisible && Strength > 0.0f) + { + SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT); + } + + // call base OnRegisterSceneNode + ISceneNode::OnRegisterSceneNode(); +} + +void CLensFlareSceneNode::render() +{ + // get the videodriver and the active camera + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + ICameraSceneNode* camera = SceneManager->getActiveCamera(); + + // return if we don't have a valid driver or a valid camera + // or if we have no texture attached to the material + if (!camera || !driver || !Material.getTexture(0)) + return; + + // get screencenter + const core::vector2d screenCenter = core::vector2d( + SceneManager->getVideoDriver()->getScreenSize().Width, + SceneManager->getVideoDriver()->getScreenSize().Height)/2; + + // get screencoordinates of the node + const core::vector2d lightPos = SceneManager->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition( + getAbsolutePosition(), + camera); + + // store old projection matrix + core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); + + // store old view matrix + core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); + + // clear the projection matrix + driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix); + + // clear the view matrix + driver->setTransform(video::ETS_VIEW, core::IdentityMatrix); + + // set the transform + driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); + + // set the material + driver->setMaterial(Material); + + // calculate some handy constants + const f32 texPos = 1.0f/EFT_COUNT; + const s32 texHeight = s32(Material.getTexture(0)->getSize().Height*0.5f); + const f32 screenWidth = f32(driver->getScreenSize().Width); + const f32 screenHeight = f32(driver->getScreenSize().Height); + + // render the flares + for (u32 i=0; iflarePos = screenCenter.getInterpolated(lightPos, -2.0*flare.Position); + + // calculate flareposition in vertex coordinates using the scalefactor of the flare + s32 flareScale = s32((texHeight*flare.Scale)); + core::rect flareRect = core::rect( + -1.f + 2.f * f32(flarePos.X-flareScale) / screenWidth, + -1.f + 2.f * f32(screenHeight-flarePos.Y-flareScale) / screenHeight, + -1.f + 2.f * f32(flarePos.X+flareScale) / screenWidth, + -1.f + 2.f * f32(screenHeight-flarePos.Y+flareScale) / screenHeight); + + // calculate flarecolor in dependence of occlusion + f32 flareAlpha = f32(flare.Color.getAlpha()) / 255.f; + video::SColor flareColor(255, + (u32)(Strength * flareAlpha * flare.Color.getRed()), + (u32)(Strength * flareAlpha * flare.Color.getGreen()), + (u32)(Strength * flareAlpha * flare.Color.getBlue())); + + // set vertex colors + Vertices[0].Color = flareColor; + Vertices[1].Color = flareColor; + Vertices[2].Color = flareColor; + Vertices[3].Color = flareColor; + + // set texture coordinates + Vertices[0].TCoords.set( flare.Type * texPos, 1.0f); + Vertices[1].TCoords.set( flare.Type * texPos, 0.0f); + Vertices[2].TCoords.set((flare.Type+1) * texPos, 0.0f); + Vertices[3].TCoords.set((flare.Type+1) * texPos, 1.0f); + + // set vertex positions + Vertices[0].Pos.set(flareRect.UpperLeftCorner.X, flareRect.UpperLeftCorner.Y, 0); + Vertices[1].Pos.set(flareRect.UpperLeftCorner.X, flareRect.LowerRightCorner .Y, 0); + Vertices[2].Pos.set(flareRect.LowerRightCorner.X, flareRect.LowerRightCorner.Y, 0); + Vertices[3].Pos.set(flareRect.LowerRightCorner.X, flareRect.UpperLeftCorner.Y, 0); + + //draw the mesh + driver->drawIndexedTriangleList(Vertices, 4, Indices, 2); + } + + // restore view matrix + driver->setTransform(video::ETS_VIEW, oldViewMat); + + // restore projection matrix + driver->setTransform(video::ETS_PROJECTION, oldProjMat); +} + +const core::aabbox3df& CLensFlareSceneNode::getBoundingBox() const +{ + // return the bounding box + return BBox; +} + +ESCENE_NODE_TYPE CLensFlareSceneNode::getType() const +{ + // return type of the scene node + // (important when using with a custom scene node factory) + return scene::ESNT_UNKNOWN; //(ESCENE_NODE_TYPE) ECSNT_LENSFLARE; +} + +void CLensFlareSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const +{ + // write attributes of the scene node. + ISceneNode::serializeAttributes(out, options); +} + +void CLensFlareSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) +{ + // read attributes of the scene node. + ISceneNode::deserializeAttributes(in, options); +} + +ISceneNode* CLensFlareSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager) +{ + if (!newParent) + newParent = Parent; + if (!newManager) + newManager = SceneManager; + + CLensFlareSceneNode* nb = new CLensFlareSceneNode(newParent, newManager, ID); + + nb->cloneMembers(this, newManager); + nb->Material = Material; + + nb->drop(); + return nb; +} + +}// end irr namespace +}// end scene namespace + diff --git a/src/graphics/lens_flare.hpp b/src/graphics/lens_flare.hpp new file mode 100644 index 000000000..50d7145b0 --- /dev/null +++ b/src/graphics/lens_flare.hpp @@ -0,0 +1,117 @@ +#ifndef _CLENSFLARESCENENODE_H +#define _CLENSFLARESCENENODE_H + +#include +#include + +namespace irr +{ +namespace scene +{ + // enum with different flare types (used by SFlareData) + enum E_FLARE_TYPE + { + EFT_SUN = 0, + EFT_GLOW, + EFT_LENS, + EFT_STREAKS, + EFT_RING, + EFT_HALO, + EFT_CIRCLE, + EFT_POLY, + EFT_COUNT + }; + + // struct holding the flare specification + struct SFlareData + { + public: + // constructor + SFlareData(const E_FLARE_TYPE type, const float position, + const float scale, const video::SColor &color) + { + Type = type; + Position = position; + Scale = scale; + Color = color; + } + + // flare type + E_FLARE_TYPE Type; + // position + f32 Position; + // flare scale + f32 Scale; + // flare color + video::SColor Color; + }; + + class CLensFlareSceneNode : public ISceneNode + { + public: + // constructor + CLensFlareSceneNode(ISceneNode* parent, scene::ISceneManager* mgr, s32 id = -1); + + // destructor + virtual ~CLensFlareSceneNode(); + + protected: + // material of the node + video::SMaterial Material; + + // Bounding box + core::aabbox3d BBox; + + // vertices and indices of a flare element + video::S3DVertex Vertices[4]; + u16 Indices[6]; + + // flare data array + core::array FlareData; + + // Strength of the flare effect (between 0 and 1) + f32 Strength; + + public: + // typical OnRegisterSceneNode function + virtual void OnRegisterSceneNode(); + + // renders the node + virtual void render(); + + // returns the bounding box + virtual const core::aabbox3d& getBoundingBox() const; + + // returns the node type + virtual ESCENE_NODE_TYPE getType() const; + + // returns the material count + virtual u32 getMaterialCount() const; + + // returns the material + virtual video::SMaterial& getMaterial(u32 i); + + // writes attributes of the scene node. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const; + + // reads attributes of the scene node. + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0); + + // clones the node + virtual ISceneNode* clone(ISceneNode* newParent = 0, ISceneManager* newManager = 0); + + // returns the flare data array + core::array& getFlareData(); + + // returns the strength (visibility) of the flares + f32 getStrength() { return Strength; } + + // sets the strength (visibility) of the flares + void setStrength(f32 strength) { Strength = core::clamp(strength, 0.0f, 0.7f); } + }; + +} // end namespace scene +} // end namespace irr + +#endif + diff --git a/src/graphics/light.cpp b/src/graphics/light.cpp new file mode 100644 index 000000000..bbc0d0da6 --- /dev/null +++ b/src/graphics/light.cpp @@ -0,0 +1,93 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/light.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +IMesh * LightNode::sphere = NULL; +SMaterial LightNode::mat; +aabbox3df LightNode::box; + + +LightNode::LightNode(scene::ISceneManager* mgr, float radius, float r, float g, float b): + ISceneNode(mgr->getRootSceneNode(), mgr, -1) +{ + if (!sphere) + { + mat.Lighting = false; + mat.MaterialType = irr_driver->getShader(ES_POINTLIGHT); + + mat.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + mat.setTexture(1, irr_driver->getRTT(RTT_DEPTH)); + + for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; ++i) + { + mat.TextureLayer[i].TextureWrapU = + mat.TextureLayer[i].TextureWrapV = ETC_CLAMP_TO_EDGE; + } + + mat.setFlag(EMF_BILINEAR_FILTER, false); + mat.setFlag(EMF_ZWRITE_ENABLE, false); + + mat.MaterialTypeParam = pack_textureBlendFunc(EBF_ONE, EBF_ONE); + mat.BlendOperation = EBO_ADD; + + sphere = mgr->getGeometryCreator()->createSphereMesh(1, 16, 16); + box = sphere->getBoundingBox(); + } + + setScale(vector3df(radius)); + m_radius = radius; + + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; +} + +LightNode::~LightNode() +{ +} + +void LightNode::render() +{ + PointLightProvider * const cb = (PointLightProvider *) irr_driver->getCallback(ES_POINTLIGHT); + cb->setColor(m_color[0], m_color[1], m_color[2]); + cb->setPosition(getPosition().X, getPosition().Y, getPosition().Z); + cb->setRadius(m_radius); + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(mat); + + drv->drawMeshBuffer(sphere->getMeshBuffer(0)); +} + +void LightNode::OnRegisterSceneNode() +{ // This node is only drawn manually. +} diff --git a/src/graphics/light.hpp b/src/graphics/light.hpp new file mode 100644 index 000000000..245a3250e --- /dev/null +++ b/src/graphics/light.hpp @@ -0,0 +1,64 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 HEADER_LIGHT_HPP +#define HEADER_LIGHT_HPP + +#include +#include + +using namespace irr; + +namespace irr +{ + namespace scene { class IMesh; } +} + +// The actual node +class LightNode: public scene::ISceneNode +{ +public: + LightNode(scene::ISceneManager* mgr, float radius, float r, float g, float b); + virtual ~LightNode(); + + virtual void render() OVERRIDE; + + virtual const core::aabbox3d& getBoundingBox() const OVERRIDE + { + return box; + } + + virtual void OnRegisterSceneNode() OVERRIDE; + + virtual u32 getMaterialCount() const OVERRIDE { return 1; } + virtual video::SMaterial& getMaterial(u32 i) OVERRIDE { return mat; } + + float getRadius() const { return m_radius; } + void getColor(float out[3]) const { memcpy(out, m_color, 3 * sizeof(float)); } + +protected: + static video::SMaterial mat; + static core::aabbox3df box; + + static scene::IMesh *sphere; + + float m_radius; + float m_color[3]; +}; + +#endif diff --git a/src/graphics/lod_node.cpp b/src/graphics/lod_node.cpp index 5c04b5aad..f8820d69d 100644 --- a/src/graphics/lod_node.cpp +++ b/src/graphics/lod_node.cpp @@ -1,6 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Marianne Gagnon -// based on code Copyright (C) 2002-2010 Nikolaus Gebhardt +// Copyright (C) 2011-2013 Marianne Gagnon +// based on code Copyright 2002-2010 Nikolaus Gebhardt // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -49,6 +49,7 @@ LODNode::LODNode(std::string group_name, scene::ISceneNode* parent, drop(); m_forced_lod = -1; + m_last_tick = 0; } LODNode::~LODNode() @@ -81,6 +82,11 @@ int LODNode::getLevel() if (dist < m_detail[n]) return n; } + + // If it's the shadow pass, and we would have otherwise hidden the item, show the min one + if (curr_cam->isOrthogonal()) + return m_detail.size() - 1; + return -1; } // getLevel @@ -139,9 +145,12 @@ void LODNode::OnRegisterSceneNode() shown = true; } + const u32 now = irr_driver->getDevice()->getTimer()->getTime(); + // support an optional, mostly hard-coded fade-in/out effect for objects with a single level if (m_nodes.size() == 1 && (m_nodes[0]->getType() == scene::ESNT_MESH || - m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH)) + m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH) && + now > m_last_tick) { if (m_previous_visibility == WAS_HIDDEN && shown) { @@ -237,6 +246,7 @@ void LODNode::OnRegisterSceneNode() } m_previous_visibility = (shown ? WAS_SHOWN : WAS_HIDDEN); + m_last_tick = now; // If this node has children other than the LOD nodes, draw them core::list::Iterator it; @@ -279,7 +289,13 @@ void LODNode::add(int level, scene::ISceneNode* node, bool reparent) if(UserConfigParams::m_hw_skinning_enabled && node->getType() == scene::ESNT_ANIMATED_MESH) HardwareSkinning::prepareNode((scene::IAnimatedMeshSceneNode*)node); + if (node->getType() == scene::ESNT_ANIMATED_MESH) + ((scene::IAnimatedMeshSceneNode *) node)->setReadOnlyMaterials(true); + if (node->getType() == scene::ESNT_MESH) + ((scene::IMeshSceneNode *) node)->setReadOnlyMaterials(true); + node->drop(); node->updateAbsolutePosition(); + irr_driver->applyObjectPassShader(node); } diff --git a/src/graphics/lod_node.hpp b/src/graphics/lod_node.hpp index 65762d057..3cca1bc28 100644 --- a/src/graphics/lod_node.hpp +++ b/src/graphics/lod_node.hpp @@ -1,6 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Marianne Gagnon -// based on code Copyright (C) 2002-2010 Nikolaus Gebhardt +// Copyright (C) 2011-2013 Marianne Gagnon +// based on code Copyright 2002-2010 Nikolaus Gebhardt // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -71,6 +71,8 @@ private: PreviousVisibility m_previous_visibility; + u32 m_last_tick; + public: LODNode(std::string group_name, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id=-1); @@ -112,8 +114,8 @@ public: std::vector& getAllNodes() { return m_nodes; } //! OnAnimate() is called just before rendering the whole scene. - /** This method will be called once per frame, independent - of whether the scene node is visible or not. */ + /** This method will be called once per frame, independent + of whether the scene node is visible or not. */ virtual void OnAnimate(u32 timeMs); virtual void OnRegisterSceneNode(); diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 18860fd93..31929007b 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2004 Steve Baker -// 2010 Steve Baker, Joerg Henrichs +// Copyright (C) 2010-2013 Steve Baker, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,8 +27,10 @@ #include "config/user_config.hpp" #include "config/stk_config.hpp" #include "guiengine/engine.hpp" +#include "graphics/callbacks.hpp" #include "graphics/irr_driver.hpp" #include "graphics/particle_kind_manager.hpp" +#include "graphics/shaders.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "utils/string_utils.hpp" @@ -36,7 +38,6 @@ #include "tracks/track.hpp" #include "utils/log.hpp" -#include #include #include #include @@ -46,351 +47,6 @@ using namespace irr::video; const unsigned int UCLAMP = 1; const unsigned int VCLAMP = 2; -//----------------------------------------------------------------------------- - -class NormalMapProvider : public video::IShaderConstantSetCallBack -{ - bool m_with_lightmap; - -public: - LEAK_CHECK() - - NormalMapProvider(bool withLightmap) - { - m_with_lightmap = withLightmap; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - s32 decaltex = 0; - services->setPixelShaderConstant("DecalTex", &decaltex, 1); - - s32 bumptex = 1; - services->setPixelShaderConstant("BumpTex", &bumptex, 1); - - s32 lightmapTex = (m_with_lightmap ? 2 : 0); - services->setPixelShaderConstant("LightMapTex", &lightmapTex, 1); - - s32 hasLightMap = (m_with_lightmap ? 1 : 0); - services->setPixelShaderConstant("HasLightMap", &hasLightMap, 1); - - // We could calculate light direction as coming from the sun (then we'd need to - // transform it into camera space). But I find that pretending light - // comes from the camera gives good results - const float lightdir[] = {0.1852f, -0.1852f, -0.9259f}; - services->setVertexShaderConstant("lightdir", lightdir, 3); - } -}; - - -//----------------------------------------------------------------------------- - -class WaterShaderProvider : public video::IShaderConstantSetCallBack -{ - float m_dx_1, m_dy_1, m_dx_2, m_dy_2; - float m_water_shader_speed_1; - float m_water_shader_speed_2; - bool m_fog; - -public: - LEAK_CHECK() - - void enableFog(bool enable) - { - m_fog = enable; - } - - - WaterShaderProvider(float water_shader_speed_1, - float water_shader_speed_2) - { - m_dx_1 = 0.0f; - m_dy_1 = 0.0f; - m_dx_2 = 0.0f; - m_dy_2 = 0.0f; - - m_water_shader_speed_1 = water_shader_speed_1/100.0f; - m_water_shader_speed_2 = water_shader_speed_2/100.0f; - - m_fog = false; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - m_dx_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1; - m_dy_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1; - - m_dx_2 += GUIEngine::getLatestDt()*m_water_shader_speed_2; - m_dy_2 -= GUIEngine::getLatestDt()*m_water_shader_speed_2; - - if (m_dx_1 > 1.0f) m_dx_1 -= 1.0f; - if (m_dy_1 > 1.0f) m_dy_1 -= 1.0f; - if (m_dx_2 > 1.0f) m_dx_2 -= 1.0f; - if (m_dy_2 < 0.0f) m_dy_2 += 1.0f; - - s32 decaltex = 0; - services->setPixelShaderConstant("DecalTex", &decaltex, 1); - - s32 bumptex = 1; - services->setPixelShaderConstant("BumpTex1", &bumptex, 1); - - bumptex = 2; - services->setPixelShaderConstant("BumpTex2", &bumptex, 1); - - // We could calculate light direction as coming from the sun (then we'd need to - // transform it into camera space). But I find that pretending light - // comes from the camera gives good results - const float lightdir[] = {-0.315f, 0.91f, -0.3f}; - services->setVertexShaderConstant("lightdir", lightdir, 3); - - services->setVertexShaderConstant("delta1", &m_dx_1, 2); - services->setVertexShaderConstant("delta2", &m_dx_2, 2); - - if (m_fog) - { - Track* t = World::getWorld()->getTrack(); - - float fogStart = t->getFogStart(); - services->setPixelShaderConstant("fogFrom", &fogStart, 1); - - float fogEnd = t->getFogEnd(); - services->setPixelShaderConstant("fogTo", &fogEnd, 1); - - video::SColor fogColor = t->getFogColor(); - float fogColorVec[] = {fogColor.getRed()/255.0f, - fogColor.getGreen()/255.0f, - fogColor.getBlue()/255.0f, 1.0f}; - services->setVertexShaderConstant("fogColor", fogColorVec, 4); - } - } -}; - -//----------------------------------------------------------------------------- - -// FIXME: refactor this hack to get per-instance properties, and apply the -// clean fix to all shaders why we're at it...... -std::map grass_shaders_times; -int grass_shaders_times_index = 0; - -class GrassShaderProvider : public video::IShaderConstantSetCallBack -{ - bool m_fog; - float m_angle; - float m_amplitude; - float m_speed; - -public: - LEAK_CHECK() - - - GrassShaderProvider(float amplitude, float speed) - { - m_fog = false; - m_angle = 0.0f; - m_amplitude = amplitude; - m_speed = speed; - } - - - void enableFog(bool enable) - { - m_fog = enable; - } - - void update(float dt) - { - m_angle += GUIEngine::getLatestDt()*m_speed; - if (m_angle > M_PI*2) m_angle -= M_PI*2; - } - - virtual void OnSetConstants(irr::video::IMaterialRendererServices *services, - s32 userData) - { - grass_shaders_times[userData] += GUIEngine::getLatestDt()*m_speed; - if (grass_shaders_times[userData] > M_PI*2) grass_shaders_times[userData] -= M_PI*2; - - services->setVertexShaderConstant("angle", &grass_shaders_times[userData], 1); - - int fog = (m_fog ? 1 : 0); - services->setVertexShaderConstant("fog", &fog, 1); - - s32 tex = 0; - services->setVertexShaderConstant("tex", &tex, 1); - - services->setVertexShaderConstant("amplitude", &m_amplitude, 1); - - if (m_fog) - { - Track* t = World::getWorld()->getTrack(); - - float fogStart = t->getFogStart(); - services->setPixelShaderConstant("fogFrom", &fogStart, 1); - - float fogEnd = t->getFogEnd(); - services->setPixelShaderConstant("fogTo", &fogEnd, 1); - - video::SColor fogColor = t->getFogColor(); - float fogColorVec[] = {fogColor.getRed()/255.0f, - fogColor.getGreen()/255.0f, - fogColor.getBlue()/255.0f, 1.0f}; - services->setVertexShaderConstant("fogColor", fogColorVec, 4); - } - } -}; - -//----------------------------------------------------------------------------- - -#if 0 -#pragma mark - -#endif - -class SplattingProvider : public video::IShaderConstantSetCallBack -{ - core::vector3df m_light_direction; - bool m_light_dir_calculated; - bool m_lightmap; - -public: - LEAK_CHECK() - - SplattingProvider(bool lightmap) - { - m_light_dir_calculated = false; - m_lightmap = lightmap; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - if (!m_light_dir_calculated) - { - m_light_dir_calculated = true; - m_light_direction = -World::getWorld()->getTrack()->getSunRotation().rotationToDirection(); - } - - s32 tex_layout = 1; - services->setPixelShaderConstant("tex_layout", &tex_layout, 1); - - s32 tex_detail0 = 2; - services->setPixelShaderConstant("tex_detail0", &tex_detail0, 1); - - s32 tex_detail1 = 3; - services->setPixelShaderConstant("tex_detail1", &tex_detail1, 1); - - s32 tex_detail2 = 4; - services->setPixelShaderConstant("tex_detail2", &tex_detail2, 1); - - s32 tex_detail3 = 5; - services->setPixelShaderConstant("tex_detail3", &tex_detail3, 1); - - if (m_lightmap) - { - s32 tex_lightmap = 6; - services->setPixelShaderConstant("tex_lightmap", &tex_lightmap, 1); - } - - services->setVertexShaderConstant("lightdir", &m_light_direction.X, 3); - } -}; - -//----------------------------------------------------------------------------- - -#if 0 -#pragma mark - -#endif - -class SphereMapProvider: public video::IShaderConstantSetCallBack -{ - core::vector3df m_light_direction; - -public: - LEAK_CHECK() - - SphereMapProvider() - { - m_light_direction = core::vector3df(-0.6f, -0.5f, -0.63f); - //m_light_direction = core::vector3df(-0.315f, 0.91f, -0.3f); - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - s32 texture = 0; - services->setPixelShaderConstant("texture", &texture, 1); - - services->setVertexShaderConstant("lightdir", &m_light_direction.X, 3); - } -}; - -//----------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -class BubbleEffectProvider : public video::IShaderConstantSetCallBack -{ - irr::u32 initial_time; - float m_transparency; - bool m_is_visible; - -public: - LEAK_CHECK() - - BubbleEffectProvider() - { - initial_time = irr_driver->getDevice()->getTimer()->getRealTime(); - m_transparency = 1.0f; - m_is_visible = true; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - if (m_is_visible && m_transparency < 1.0f) - { - m_transparency += GUIEngine::getLatestDt()*0.3f; - if (m_transparency > 1.0f) m_transparency = 1.0f; - } - else if (!m_is_visible && m_transparency > 0.0f) - { - m_transparency -= GUIEngine::getLatestDt()*0.3f; - if (m_transparency < 0.0f) m_transparency = 0.0f; - } - - float time = (irr_driver->getDevice()->getTimer()->getRealTime() - initial_time) / 1000.0f; - services->setVertexShaderConstant("time", &time, 1); - services->setVertexShaderConstant("transparency", &m_transparency, 1); - } - - void onMadeVisible() - { - m_is_visible = true; - } - - void onHidden() - { - m_is_visible = false; - m_transparency = 0.0f; - } - - void isInitiallyHidden() - { - m_is_visible = false; - m_transparency = 0.0f; - } -}; - -#if 0 -#pragma mark - -#endif - //----------------------------------------------------------------------------- /** Create a new material using the parameters specified in the xml file. * \param node Node containing the parameters for this material. @@ -430,8 +86,9 @@ Material::Material(const XMLNode *node, int index, bool deprecated) else if (s=="" || s=="none") m_adjust_image = ADJ_NONE; else - printf("Incorrect adjust-image specification: '%s' - ignored.\n", - s.c_str()); + Log::warn("material", + "Incorrect adjust-image specification: '%s' - ignored.", + s.c_str()); node->get("alpha", &m_alpha_blending ); node->get("light", &m_lighting ); @@ -458,9 +115,14 @@ Material::Material(const XMLNode *node, int index, bool deprecated) { m_collision_reaction = PUSH_BACK; } + else if (creaction == "push-soccer") + { + m_collision_reaction = PUSH_SOCCER_BALL; + } else if (creaction.size() > 0) { - fprintf(stderr, "[Material] WARNING: Unknown collision reaction '%s'\n", creaction.c_str()); + Log::warn("Material","Unknown collision reaction '%s'", + creaction.c_str()); } node->get("below-surface", &m_below_surface ); @@ -562,16 +224,19 @@ Material::Material(const XMLNode *node, int index, bool deprecated) node->get("splatting-texture-2", &m_splatting_texture_2); node->get("splatting-texture-3", &m_splatting_texture_3); node->get("splatting-texture-4", &m_splatting_texture_4); - node->get("splatting-lightmap", &m_splatting_lightmap); + } + else if (s == "caustics") + { + m_graphical_effect = GE_CAUSTICS; } else if (s == "none") { } else if (s != "") { - fprintf(stderr, - "Invalid graphical effect specification: '%s' - ignored.\n", - s.c_str()); + Log::warn("material", + "Invalid graphical effect specification: '%s' - ignored.", + s.c_str()); } else { @@ -591,7 +256,8 @@ Material::Material(const XMLNode *node, int index, bool deprecated) } else { - fprintf(stderr, "[Material] WARNING: could not find normal map image in materials.xml\n"); + Log::warn("material", + "Could not find normal map image in materials.xml"); } node->get("normal-light-map", &m_normal_map_shader_lightmap); @@ -614,9 +280,8 @@ Material::Material(const XMLNode *node, int index, bool deprecated) else if (s == "additive") m_add = true; else if (s == "coverage") m_alpha_to_coverage = true; else if (s != "none") - fprintf(stderr, - "[Material] WARNING: Unknown compositing mode '%s'\n", - s.c_str()); + Log::warn("material", "Unknown compositing mode '%s'", + s.c_str()); } @@ -661,10 +326,9 @@ Material::Material(const XMLNode *node, int index, bool deprecated) } else { - fprintf(stderr, - "[Material] WARNING: unknown node type '%s' for texture " - "'%s' - ignored.\n", - child_node->getName().c_str(), m_texname.c_str()); + Log::warn("material", "Unknown node type '%s' for texture " + "'%s' - ignored.", + child_node->getName().c_str(), m_texname.c_str()); } } // for i getNumNodes() @@ -735,8 +399,6 @@ void Material::init(unsigned int index) m_water_splash = false; m_is_jump_texture = false; - m_shaders.resize(SHADER_COUNT, NULL); - for (int n=0; ngetTexture(full_path, + isPreMul(), + isPreDiv(), + complain_if_not_found); } - - - m_texture = irr_driver->getTexture(full_path, - isPreMul(), - isPreDiv(), - complain_if_not_found); if (m_texture == NULL) return; @@ -776,8 +440,8 @@ void Material::install(bool is_full_path, bool complain_if_not_found) } else { - fprintf(stderr, "Applying mask failed for '%s'!\n", - m_texname.c_str()); + Log::warn("material", "Applying mask failed for '%s'!", + m_texname.c_str()); } } m_texture->grab(); @@ -793,20 +457,6 @@ Material::~Material() irr_driver->removeTexture(m_texture); } - for (unsigned int n=0; ndrop(); - } - } - - for (std::map::iterator it = m_bubble_provider.begin(); - it != m_bubble_provider.end(); it++) - { - it->second->drop(); - } - // If a special sfx is installed (that isn't part of stk itself), the // entry needs to be removed from the sfx_manager's mapping, since other // tracks might use the same name. @@ -829,8 +479,8 @@ void Material::initCustomSFX(const XMLNode *sfx) if (filename.empty()) { - fprintf(stderr, "[Material] WARNING: sfx node has no 'filename' " - "attribute, sound effect will be ignored\n"); + Log::warn("material", "Sfx node has no 'filename' " + "attribute, sound effect will be ignored."); return; } @@ -908,10 +558,10 @@ void Material::initParticlesEffect(const XMLNode *node) if (count == 0) { - fprintf(stderr, "[Material::initParticlesEffect] WARNING: Particles " - "'%s' for material '%s' are declared but not used " - "(no emission condition set)\n", - base.c_str(), m_texname.c_str()); + Log::warn("material", "initParticlesEffect: Particles " + "'%s' for material '%s' are declared but not used " + "(no emission condition set).", + base.c_str(), m_texname.c_str()); } for (int c=0; cgetTexture(0) != NULL && ((core::stringc)m->getTexture(0)->getName()).find("deprecated") != -1)) + if (m_deprecated || + (m->getTexture(0) != NULL && + ((core::stringc)m->getTexture(0)->getName()).find("deprecated") != -1)) { - fprintf(stderr, "WARNING: track uses deprecated texture <%s>\n", m_texname.c_str()); + Log::warn("material", "Track uses deprecated texture '%s'\n", + m_texname.c_str()); } @@ -1022,24 +675,10 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m } if (m_smooth_reflection_shader) { - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { - if (m_shaders[SHADER_SPHERE_MAP] == NULL) - { - m_shaders[SHADER_SPHERE_MAP] = new SphereMapProvider(); + m->MaterialType = irr_driver->getShader(ES_SPHERE_MAP); } - // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "spheremap.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "spheremap.frag").c_str(), - "main", video::EPST_PS_2_0, - m_shaders[SHADER_SPHERE_MAP], video::EMT_SOLID_2_LAYER ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; - } else { m->MaterialType = video::EMT_SPHERE_MAP; @@ -1058,6 +697,12 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m } if (m_graphical_effect == GE_SPHERE_MAP) { + if (irr_driver->isGLSL()) + { + m->MaterialType = irr_driver->getShader(ES_SPHERE_MAP); + } + else + { m->MaterialType = video::EMT_SPHERE_MAP; // sphere map + alpha blending is a supported combination so in @@ -1071,7 +716,8 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m modes++; } } -#if !LIGHTMAP_VISUALISATION + } + if (m_lightmap) { m->MaterialType = video::EMT_LIGHTMAP; @@ -1082,7 +728,7 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m m->MaterialType = video::EMT_LIGHTMAP_ADD; modes++; } -#endif + if (m_add) { //m->MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; @@ -1101,8 +747,7 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m if (m_graphical_effect == GE_NORMAL_MAP) { IVideoDriver* video_driver = irr_driver->getVideoDriver(); - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { ITexture* tex = irr_driver->getTexture(m_normal_map_tex); if (m_is_heightmap) @@ -1120,37 +765,9 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m with_lightmap = true; } - if (with_lightmap) - { - if (m_shaders[SHADER_NORMAL_MAP_WITH_LIGHTMAP] == NULL) - { - m_shaders[SHADER_NORMAL_MAP_WITH_LIGHTMAP] = - new NormalMapProvider(true); - } - } - else - { - if (m_shaders[SHADER_NORMAL_MAP] == NULL) - { - m_shaders[SHADER_NORMAL_MAP] = new NormalMapProvider(false); - } - } - - const char* vertex_shader = "normalmap.vert"; - const char* pixel_shader = "normalmap.frag"; - // Material and shaders - IGPUProgrammingServices* gpu = - video_driver->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + vertex_shader).c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + pixel_shader).c_str(), - "main", video::EPST_PS_2_0, - m_shaders[with_lightmap ? SHADER_NORMAL_MAP_WITH_LIGHTMAP - : SHADER_NORMAL_MAP], - video::EMT_SOLID_2_LAYER ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader( + with_lightmap ? ES_NORMAL_MAP_LIGHTMAP : ES_NORMAL_MAP ); m->Lighting = false; m->ZWriteEnable = true; @@ -1200,64 +817,20 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m } m->setTexture(5, tex); - if (m_splatting_lightmap.size() > 0) - { - tex = irr_driver->getTexture(m_splatting_lightmap); - } - m->setTexture(6, tex); - - if (m_splatting_lightmap.size() > 0) - { - if (m_shaders[SHADER_SPLATTING_LIGHTMAP] == NULL) - { - m_shaders[SHADER_SPLATTING_LIGHTMAP] = - new SplattingProvider(true); - } - } - else - { - if (m_shaders[SHADER_SPLATTING] == NULL) - { - m_shaders[SHADER_SPLATTING] = new SplattingProvider(false); - } - } - - // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - - if (m_splatting_lightmap.size() > 0) - { - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() - + "splatting_lightmap.vert").c_str(), - "main",video::EVST_VS_2_0, - (file_manager->getShaderDir() - + "splatting_lightmap.frag").c_str(), - "main",video::EPST_PS_2_0, - m_shaders[SHADER_SPLATTING_LIGHTMAP], - video::EMT_SOLID ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader(ES_SPLATTING); } else { - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() - + "splatting.vert").c_str(), - "main",video::EVST_VS_2_0, - (file_manager->getShaderDir() - + "splatting.frag").c_str(), - "main",video::EPST_PS_2_0, - m_shaders[SHADER_SPLATTING], video::EMT_SOLID ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = video::EMT_SOLID; } } - else + if (m_graphical_effect == GE_CAUSTICS && irr_driver->isGLSL()) { - m->MaterialType = video::EMT_SOLID; + m->MaterialType = irr_driver->getShader(ES_CAUSTICS); + + m->setTexture(1, irr_driver->getTexture((file_manager->getTextureDir() + "caustics.png").c_str())); } - } // Modify lightmap materials so that vertex colors are taken into account. @@ -1274,26 +847,14 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m if (m_graphical_effect == GE_BUBBLE && mb != NULL) { - IVideoDriver* video_driver = irr_driver->getVideoDriver(); - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { - if (m_bubble_provider.find(mb) == m_bubble_provider.end()) - { - m_bubble_provider[mb] = new BubbleEffectProvider(); - } + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + bubble->addBubble(mb); - // Material and shaders - IGPUProgrammingServices* gpu = video_driver->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "bubble.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "bubble.frag").c_str(), - "main", video::EPST_PS_2_0, - m_bubble_provider[mb], - (m_alpha_blending ? video::EMT_TRANSPARENT_ALPHA_CHANNEL - : video::EMT_SOLID) ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader(ES_BUBBLES); + m->BlendOperation = video::EBO_ADD; // alpha blending and bubble shading can work together so when both are enabled // don't increment the 'modes' counter to not get the 'too many modes' warning @@ -1307,70 +868,40 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m if (m_graphical_effect == GE_WATER_SHADER) { - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { - if (m_shaders[SHADER_WATER] == NULL) - { - m_shaders[SHADER_WATER] = - new WaterShaderProvider(m_water_shader_speed_1, - m_water_shader_speed_2); - } - m->setTexture(1, irr_driver->getTexture(file_manager->getTextureFile("waternormals.jpg"))); m->setTexture(2, irr_driver->getTexture(file_manager->getTextureFile("waternormals2.jpg"))); - bool fog = World::getWorld()->getTrack()->isFogEnabled(); - const char* vertex_shader = (fog ? "water_fog.vert" : "water.vert"); - const char* pixel_shader = (fog ? "water_fog.frag" : "water.frag"); + ((WaterShaderProvider *) irr_driver->getCallback(ES_WATER))-> + setSpeed(m_water_shader_speed_1/100.0f, m_water_shader_speed_2/100.0f); - ((WaterShaderProvider*)m_shaders[SHADER_WATER])->enableFog(fog); - - // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + vertex_shader).c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + pixel_shader ).c_str(), - "main", video::EPST_PS_2_0, - m_shaders[SHADER_WATER], - video::EMT_TRANSPARENT_ALPHA_CHANNEL); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader(ES_WATER); } modes++; } if (m_graphical_effect == GE_GRASS) { - if (UserConfigParams::m_pixel_shaders && + if (UserConfigParams::m_weather_effects && irr_driver->isGLSL()) { - if (m_shaders[SHADER_GRASS] == NULL) - { - m_shaders[SHADER_GRASS] = - new GrassShaderProvider(m_grass_amplitude, m_grass_speed); - } - - bool fog = World::getWorld()->getTrack()->isFogEnabled(); - ((GrassShaderProvider*)m_shaders[SHADER_GRASS])->enableFog(fog); - - grass_shaders_times[grass_shaders_times_index] = (rand() % 500)/500.0f * M_PI * 2.0f; + // Only one grass speed & amplitude per map for now + ((GrassShaderProvider *) irr_driver->getCallback(ES_GRASS))-> + setSpeed(m_grass_speed); + ((GrassShaderProvider *) irr_driver->getCallback(ES_GRASS))-> + setAmplitude(m_grass_amplitude); // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "grass.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "grass.frag").c_str(), - "main", video::EPST_PS_2_0, - m_shaders[SHADER_GRASS], - video::EMT_TRANSPARENT_ALPHA_CHANNEL, - grass_shaders_times_index); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + if (m_alpha_testing) + { + m->MaterialType = irr_driver->getShader(ES_GRASS_REF); + } + else { + m->MaterialType = irr_driver->getShader(ES_GRASS); + m->BlendOperation = video::EBO_ADD; + } - grass_shaders_times_index++; } } @@ -1422,11 +953,11 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m { /** //! Texture is clamped to the last pixel - ETC_CLAMP, - //! Texture is clamped to the edge pixel - ETC_CLAMP_TO_EDGE, - //! Texture is clamped to the border pixel (if exists) - ETC_CLAMP_TO_BORDER, + ETC_CLAMP, + //! Texture is clamped to the edge pixel + ETC_CLAMP_TO_EDGE, + //! Texture is clamped to the border pixel (if exists) + ETC_CLAMP_TO_BORDER, */ for (unsigned int n=0; nisGLSL()) + return; + m->setFlag(video::EMF_FOG_ENABLE, m_fog && use_fog); if (parent != NULL) @@ -1478,10 +1013,13 @@ void Material::adjustForFog(scene::ISceneNode* parent, video::SMaterial *m, /** Callback from LOD nodes to create some effects */ void Material::onMadeVisible(scene::IMeshBuffer* who) { - if (m_bubble_provider.find(who) != m_bubble_provider.end()) - { - m_bubble_provider[who]->onMadeVisible(); - } + if (!irr_driver->isGLSL()) return; + + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + + if (bubble != NULL) + bubble->onMadeVisible(who); } //----------------------------------------------------------------------------- @@ -1489,20 +1027,24 @@ void Material::onMadeVisible(scene::IMeshBuffer* who) /** Callback from LOD nodes to create some effects */ void Material::onHidden(scene::IMeshBuffer* who) { - if (m_bubble_provider.find(who) != m_bubble_provider.end()) - { - m_bubble_provider[who]->onHidden(); - } + if (!irr_driver->isGLSL()) return; + + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + if (bubble != NULL) + bubble->onHidden(who); } //----------------------------------------------------------------------------- void Material::isInitiallyHidden(scene::IMeshBuffer* who) { - if (m_bubble_provider.find(who) != m_bubble_provider.end()) - { - m_bubble_provider[who]->isInitiallyHidden(); - } + if (!irr_driver->isGLSL()) return; + + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + if (bubble != NULL) + bubble->isInitiallyHidden(who); } //----------------------------------------------------------------------------- diff --git a/src/graphics/material.hpp b/src/graphics/material.hpp index 104375815..c56bc980a 100644 --- a/src/graphics/material.hpp +++ b/src/graphics/material.hpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2004 Steve Baker +// Copyright (C) 2010-2013 Steve Baker, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,8 +28,6 @@ #include -#define LIGHTMAP_VISUALISATION 0 - namespace irr { @@ -41,10 +40,6 @@ class XMLNode; class SFXBase; class ParticleKind; -class NormalMapProvider; -class SplattingProvider; -class BubbleEffectProvider; - /** * \ingroup graphics */ @@ -59,7 +54,8 @@ public: GE_WATER_SHADER, GE_SPHERE_MAP, GE_SPLATTING, - GE_NORMAL_MAP}; + GE_NORMAL_MAP, + GE_CAUSTICS}; enum ParticleConditions { @@ -73,23 +69,12 @@ public: { NORMAL, RESCUE, - PUSH_BACK + PUSH_BACK, + PUSH_SOCCER_BALL }; private: - enum Shaders - { - SHADER_NORMAL_MAP, - SHADER_NORMAL_MAP_WITH_LIGHTMAP, - SHADER_SPLATTING, - SHADER_WATER, - SHADER_SPHERE_MAP, - SHADER_SPLATTING_LIGHTMAP, - SHADER_GRASS, - SHADER_COUNT - }; - video::ITexture *m_texture; unsigned int m_index; std::string m_texname; @@ -232,13 +217,6 @@ private: /** If m_splatting is true, indicates the fourth splatting texture */ std::string m_splatting_texture_4; - std::string m_splatting_lightmap; - - std::vector m_shaders; - - /** Only used if bubble effect is enabled */ - std::map m_bubble_provider; - bool m_deprecated; void init (unsigned int index); diff --git a/src/graphics/material_manager.cpp b/src/graphics/material_manager.cpp index 98064fdd3..553ec4a22 100644 --- a/src/graphics/material_manager.cpp +++ b/src/graphics/material_manager.cpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2010-2013 Steve Baker, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -59,10 +60,6 @@ MaterialManager::~MaterialManager() m_materials.clear(); } // ~MaterialManager -#if LIGHTMAP_VISUALISATION -std::set g_processed; -#endif - //----------------------------------------------------------------------------- Material* MaterialManager::getMaterialFor(video::ITexture* t, diff --git a/src/graphics/material_manager.hpp b/src/graphics/material_manager.hpp index 36fa2fdbb..27184e92a 100644 --- a/src/graphics/material_manager.hpp +++ b/src/graphics/material_manager.hpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2010-2013 Steve Baker, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/mesh_tools.cpp b/src/graphics/mesh_tools.cpp index ddf4ff12a..ff44674e3 100644 --- a/src/graphics/mesh_tools.cpp +++ b/src/graphics/mesh_tools.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/mesh_tools.hpp b/src/graphics/mesh_tools.hpp index 3d4df784b..7d8d6337c 100644 --- a/src/graphics/mesh_tools.hpp +++ b/src/graphics/mesh_tools.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/mlaa_areamap.hpp b/src/graphics/mlaa_areamap.hpp new file mode 100644 index 000000000..f5c636f68 --- /dev/null +++ b/src/graphics/mlaa_areamap.hpp @@ -0,0 +1,200 @@ +#ifndef AREAMAP_H +#define AREAMAP_H + +static const unsigned char AreaMap33[] = { +0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, +0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, +0x08, 0x04, 0x00, 0x00, 0x00, 0x97, 0x22, 0xf5, 0x51, 0x00, 0x00, 0x00, +0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, +0x00, 0x02, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x87, 0x8f, 0xcc, 0xbf, +0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, +0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, +0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xdb, 0x06, 0x07, 0x09, 0x17, 0x12, +0x5f, 0x51, 0x45, 0x04, 0x00, 0x00, 0x08, 0x87, 0x49, 0x44, 0x41, 0x54, +0x78, 0xda, 0xed, 0x5d, 0x4b, 0x6e, 0xdb, 0x48, 0x14, 0x2c, 0x89, 0x2f, +0x4e, 0xe2, 0x09, 0x30, 0x9b, 0x38, 0x5b, 0xf9, 0x24, 0x73, 0xbb, 0xb9, +0x49, 0x6e, 0x32, 0x67, 0x98, 0x7d, 0xbc, 0xf2, 0xc2, 0x18, 0x78, 0x31, +0x81, 0xc6, 0xa6, 0xc4, 0xd7, 0xc3, 0x8f, 0x1a, 0x0d, 0x9a, 0x92, 0x5b, +0x64, 0x91, 0x7a, 0x1d, 0xa1, 0xab, 0x41, 0x28, 0x41, 0x80, 0x42, 0xd5, +0x63, 0x93, 0x32, 0x9c, 0x42, 0x35, 0xc0, 0x63, 0x03, 0x73, 0x6c, 0x60, +0x8f, 0x35, 0x78, 0xdc, 0xe3, 0x16, 0x2c, 0x78, 0x09, 0x57, 0x31, 0xca, +0x6f, 0xb8, 0xc3, 0x0d, 0x68, 0xd8, 0x4b, 0xb0, 0x1f, 0xe5, 0x2d, 0xbe, +0xe2, 0x77, 0x08, 0x48, 0xd8, 0x4b, 0xb0, 0x1f, 0xe5, 0x47, 0x7c, 0xa9, +0x7d, 0xfc, 0x86, 0x02, 0x06, 0x48, 0x46, 0x02, 0x64, 0x16, 0x8e, 0xcf, +0xb5, 0x8b, 0x17, 0x54, 0xd8, 0x42, 0x41, 0xc0, 0x5a, 0x82, 0xfd, 0x28, +0x0b, 0x7c, 0xac, 0x9d, 0xdc, 0x62, 0x07, 0xc5, 0x0b, 0xe1, 0xc4, 0x5c, +0x82, 0xfd, 0x28, 0xd7, 0xb8, 0xa9, 0x9d, 0x7c, 0xc2, 0x2b, 0xf6, 0x70, +0x78, 0x25, 0x9c, 0x98, 0x4b, 0xb0, 0x1f, 0xa5, 0x1c, 0x9c, 0xec, 0xa1, +0x50, 0x94, 0x70, 0x20, 0x60, 0x27, 0xc1, 0x7e, 0x94, 0x2b, 0x14, 0xf8, +0x50, 0xaf, 0xda, 0x4b, 0xeb, 0x04, 0x84, 0x13, 0x73, 0x09, 0xf6, 0xa3, +0x94, 0xd6, 0x47, 0x59, 0x3b, 0x51, 0x28, 0x1c, 0x76, 0x84, 0x13, 0x53, +0x09, 0xf6, 0xa3, 0x2c, 0xf0, 0xa1, 0xf5, 0xb2, 0xc7, 0xbe, 0xf5, 0xe1, +0xb0, 0x27, 0x9c, 0x98, 0x49, 0xb0, 0x1f, 0xe5, 0x1a, 0xd2, 0x38, 0xf1, +0x3e, 0x5a, 0x27, 0x20, 0x9c, 0x18, 0x4a, 0xb0, 0x1f, 0x65, 0xe7, 0x44, +0xda, 0x6d, 0x51, 0x1d, 0x9c, 0x38, 0x54, 0x84, 0x13, 0x13, 0x09, 0xf6, +0xa3, 0xf4, 0x4e, 0xe4, 0xe0, 0xc4, 0xfb, 0x00, 0xe1, 0xc4, 0x40, 0x82, +0xfd, 0x28, 0x81, 0x35, 0x8a, 0x6e, 0xb5, 0x3e, 0x2a, 0x54, 0x84, 0x13, +0x53, 0x09, 0xf6, 0xbb, 0xd2, 0x6f, 0x8a, 0xe0, 0x26, 0xf8, 0x70, 0xa8, +0xc0, 0x23, 0x7d, 0x09, 0x90, 0x79, 0x9c, 0xf4, 0x3c, 0x54, 0xa8, 0xda, +0x4f, 0x77, 0x58, 0x20, 0x9c, 0x18, 0x48, 0xb0, 0x1f, 0xa5, 0xf7, 0xb2, +0x46, 0xd8, 0x14, 0xda, 0xba, 0x00, 0xe1, 0xc4, 0x48, 0x82, 0xfd, 0x03, +0x1e, 0x9e, 0x30, 0x6d, 0x2f, 0x47, 0x6c, 0x8b, 0x5f, 0x4d, 0x02, 0x64, +0x36, 0x27, 0xc1, 0xc5, 0x3a, 0xf8, 0x68, 0x2e, 0x62, 0x5b, 0x18, 0x48, +0xb0, 0x1e, 0x25, 0x06, 0x9b, 0xa2, 0x7a, 0xe3, 0xc3, 0x2d, 0xe8, 0x84, +0x90, 0x90, 0xe6, 0xbb, 0xb2, 0xbf, 0x0a, 0x68, 0xbb, 0x0a, 0xb8, 0x76, +0x15, 0x40, 0xfb, 0xa9, 0x60, 0xc1, 0x4b, 0x48, 0xfb, 0x5d, 0x39, 0xd8, +0x14, 0x55, 0xf7, 0x19, 0x5e, 0x54, 0x07, 0x2f, 0x20, 0x9c, 0x18, 0x48, +0xb0, 0x7a, 0xc0, 0xfb, 0x4e, 0xb4, 0x59, 0xfe, 0x6f, 0xde, 0x05, 0xe1, +0x24, 0x79, 0x09, 0x32, 0xf3, 0x17, 0xa8, 0x77, 0x12, 0xf4, 0xf7, 0x9f, +0x2e, 0x47, 0x38, 0x31, 0x90, 0x60, 0xbd, 0x2b, 0x87, 0xab, 0xf0, 0x5e, +0x88, 0x6d, 0x91, 0xbc, 0x04, 0x99, 0xd7, 0xc5, 0x09, 0x2f, 0xae, 0xbd, +0xc2, 0x96, 0x70, 0x11, 0x27, 0x56, 0x12, 0xec, 0xbf, 0x76, 0x06, 0x4f, +0x58, 0x70, 0x13, 0x5e, 0x53, 0x7d, 0x17, 0x88, 0x38, 0xb1, 0x95, 0x60, +0xbf, 0x2b, 0x7b, 0x0e, 0x56, 0x6f, 0x36, 0x44, 0xe1, 0x5d, 0xb4, 0x9f, +0x8e, 0x70, 0x62, 0x20, 0xc1, 0xe0, 0x5d, 0x79, 0xf4, 0x09, 0x53, 0xac, +0xfb, 0xef, 0x7d, 0x62, 0x5b, 0x24, 0x2c, 0x41, 0xe6, 0x77, 0x32, 0xfc, +0x1a, 0xed, 0xd4, 0x7b, 0x3f, 0xde, 0x07, 0xe1, 0x24, 0x49, 0x09, 0x32, +0xf7, 0x4f, 0xc8, 0xde, 0x4b, 0xcf, 0x55, 0x70, 0xb1, 0x1e, 0xf8, 0x70, +0x70, 0xd7, 0x21, 0x61, 0x85, 0x0d, 0xee, 0xf1, 0x0d, 0xb7, 0xf8, 0x08, +0x41, 0x71, 0xb8, 0x9d, 0x61, 0x85, 0x37, 0x3a, 0xfc, 0xe7, 0xe0, 0x4f, +0x7f, 0x61, 0x45, 0x32, 0xfc, 0x09, 0x12, 0x9b, 0xcd, 0x03, 0xe9, 0x62, +0xf3, 0x9d, 0x65, 0x10, 0x3c, 0xe0, 0x09, 0x77, 0xf8, 0x8a, 0x2f, 0xf8, +0x5c, 0xd3, 0xdc, 0xb4, 0x44, 0x85, 0x27, 0xeb, 0x11, 0x22, 0x90, 0xf6, +0xc8, 0x57, 0xf8, 0x1b, 0x3f, 0x28, 0x06, 0x1a, 0xf7, 0x0f, 0x4f, 0x4f, +0x5b, 0xca, 0x05, 0xcf, 0x20, 0x00, 0xb6, 0x78, 0xc4, 0xb6, 0xcd, 0xd4, +0x75, 0x24, 0x1f, 0x5a, 0x1a, 0x69, 0x49, 0x8a, 0x37, 0x3f, 0x5d, 0xe0, +0x08, 0xe1, 0x37, 0xfc, 0xc0, 0x3f, 0x0c, 0xc3, 0x2c, 0x51, 0xd5, 0xed, +0xe3, 0x63, 0x49, 0x68, 0xe0, 0x19, 0x04, 0x0d, 0x4a, 0x3c, 0xd7, 0xd7, +0x0b, 0x6e, 0xf1, 0xa9, 0x26, 0xf9, 0xd0, 0xae, 0x8e, 0xa4, 0xe8, 0xd1, +0xd4, 0xd7, 0x60, 0x75, 0x39, 0xd1, 0x2d, 0xfe, 0x25, 0x18, 0x38, 0x78, +0x09, 0xe5, 0xf3, 0xf3, 0x7e, 0xb2, 0x06, 0x9e, 0x41, 0xd0, 0x61, 0x8f, +0x9f, 0xa8, 0xb0, 0xc3, 0x6b, 0x4d, 0x72, 0x53, 0x2f, 0x39, 0x90, 0x14, +0x07, 0x1a, 0xbf, 0x56, 0x43, 0xa2, 0x43, 0x4e, 0xb4, 0xc4, 0x7f, 0x93, +0x19, 0x58, 0x78, 0x09, 0xfb, 0x9f, 0x3f, 0xab, 0x89, 0x1a, 0x78, 0x06, +0x81, 0x47, 0x97, 0xf2, 0xdc, 0xd7, 0xeb, 0x23, 0xca, 0xf6, 0x7e, 0x88, +0x7f, 0x5f, 0xf4, 0x49, 0x06, 0xf7, 0xe5, 0x16, 0x3e, 0x27, 0xfa, 0x32, +0x8d, 0x61, 0xc6, 0xa8, 0x6a, 0xb5, 0xdd, 0xea, 0x24, 0x0d, 0x73, 0x30, +0x04, 0x68, 0xcd, 0xe4, 0xa0, 0x0d, 0x49, 0x4b, 0x25, 0x81, 0x24, 0xdc, +0x93, 0x01, 0xc9, 0xba, 0x97, 0x13, 0x2d, 0xa7, 0x30, 0xcc, 0x1a, 0x55, +0xd5, 0x97, 0x17, 0x9d, 0xa0, 0x81, 0x67, 0x90, 0x9e, 0x22, 0xc5, 0x2b, +0xb4, 0x59, 0x2d, 0x85, 0xbf, 0x1f, 0xd2, 0x12, 0x78, 0x8a, 0x21, 0x4d, +0x3f, 0x27, 0xba, 0x1b, 0xcf, 0x30, 0x73, 0x54, 0x55, 0x5f, 0x5f, 0x75, +0xb4, 0x06, 0x9e, 0x41, 0xd0, 0x87, 0xa2, 0x04, 0xd0, 0xd1, 0x54, 0x6d, +0xf8, 0xa6, 0x40, 0xd5, 0x11, 0x9c, 0x22, 0x19, 0xe4, 0x44, 0x77, 0x63, +0x19, 0x66, 0x8f, 0xaa, 0x6a, 0x59, 0xba, 0x91, 0x1a, 0x78, 0x06, 0xc1, +0x5b, 0x38, 0x94, 0x70, 0xf5, 0xf2, 0x34, 0xd2, 0x50, 0x44, 0xb6, 0xf7, +0xdb, 0x9c, 0xe8, 0x6e, 0x1c, 0xc3, 0x02, 0x51, 0x55, 0x57, 0x8f, 0x62, +0x94, 0x06, 0x9e, 0x41, 0x30, 0x84, 0xeb, 0x8d, 0x42, 0xfd, 0xff, 0xce, +0xfb, 0x3b, 0xe3, 0x29, 0x02, 0xd1, 0x91, 0x9c, 0xe8, 0x7e, 0x0c, 0xc3, +0x22, 0x51, 0x55, 0xb7, 0xdb, 0xb9, 0x11, 0x1a, 0x78, 0x06, 0xc1, 0x11, +0xb4, 0xa3, 0x00, 0x3c, 0x8d, 0x34, 0x34, 0xed, 0x52, 0xac, 0x9b, 0xab, +0x4f, 0x72, 0x22, 0x27, 0x5a, 0xa1, 0x3c, 0x97, 0x61, 0xa1, 0xa8, 0xaa, +0xdb, 0xef, 0xdd, 0xd9, 0x2e, 0x78, 0x06, 0xc1, 0x71, 0x74, 0xfb, 0xca, +0x2f, 0xf5, 0x2b, 0x50, 0x04, 0xa2, 0x13, 0x39, 0xd1, 0x12, 0xd5, 0xb9, +0x0c, 0x8b, 0x45, 0x55, 0x9b, 0x51, 0x9c, 0xa9, 0x81, 0x67, 0x10, 0x1c, +0x87, 0x8f, 0x7f, 0x79, 0x0a, 0xff, 0x59, 0x34, 0x57, 0x9f, 0xe2, 0x9d, +0x9c, 0xa8, 0x62, 0x77, 0x0e, 0xc3, 0x82, 0x51, 0x55, 0x57, 0x55, 0xee, +0x2c, 0x17, 0x3c, 0x83, 0x00, 0x91, 0x61, 0x06, 0x1a, 0x45, 0xd1, 0xfd, +0x8e, 0xaa, 0x4f, 0xf1, 0x4e, 0x4e, 0x74, 0x07, 0xc5, 0x3e, 0xce, 0xb0, +0x68, 0x54, 0xb5, 0x1d, 0x45, 0x5c, 0x03, 0xcf, 0x20, 0x40, 0x64, 0x98, +0x2d, 0x1b, 0x9c, 0x5f, 0x03, 0x8a, 0xf5, 0xbb, 0x39, 0xd1, 0x3d, 0x14, +0xbb, 0x18, 0xc3, 0x62, 0x51, 0xd5, 0x30, 0x8a, 0xa8, 0x0b, 0x9e, 0x41, +0x80, 0xe8, 0x30, 0x71, 0x94, 0xa2, 0x80, 0xeb, 0xde, 0x17, 0x91, 0x9c, +0xe8, 0x2e, 0xce, 0xb0, 0x78, 0x54, 0xd5, 0x55, 0x51, 0x0d, 0x3c, 0x83, +0x20, 0x8e, 0xaa, 0xa5, 0xf0, 0x44, 0x45, 0x77, 0x75, 0x44, 0xed, 0x67, +0x3c, 0x27, 0x1a, 0x67, 0x58, 0x3e, 0xaa, 0x1a, 0xd1, 0xc0, 0x33, 0x08, +0xce, 0x81, 0xbf, 0x23, 0x05, 0xd0, 0x11, 0xf4, 0x62, 0x0f, 0x91, 0x9c, +0x68, 0x9c, 0xe1, 0x42, 0x51, 0xd5, 0x88, 0x06, 0x96, 0x41, 0x80, 0x11, +0xc3, 0x0c, 0x34, 0xcd, 0xea, 0x48, 0xf4, 0x9c, 0x9c, 0x68, 0x84, 0xe1, +0x52, 0x51, 0xd5, 0x77, 0x34, 0xf0, 0x0c, 0x82, 0x73, 0x51, 0xf5, 0xee, +0x87, 0x27, 0x51, 0xac, 0xe3, 0x39, 0xd1, 0x18, 0xc3, 0x05, 0xa3, 0xaa, +0x27, 0x35, 0xf0, 0x0c, 0x82, 0xf3, 0x51, 0x21, 0x44, 0x1b, 0x3c, 0xf7, +0x1a, 0xee, 0xbc, 0x9c, 0x68, 0x84, 0xe1, 0x72, 0x51, 0xd5, 0x13, 0x1a, +0x78, 0x06, 0xc1, 0x18, 0x28, 0x02, 0x05, 0x0e, 0x44, 0xeb, 0xb1, 0x39, +0xd1, 0x21, 0xc3, 0x85, 0xa3, 0xaa, 0x47, 0x35, 0xf0, 0x0c, 0x82, 0x71, +0x50, 0x04, 0xb8, 0x6e, 0x11, 0x39, 0x51, 0xff, 0xaf, 0x97, 0x8e, 0xaa, +0x1e, 0xd1, 0xc0, 0x33, 0x08, 0x30, 0x71, 0x98, 0xce, 0xaf, 0xa9, 0x39, +0xd1, 0xc0, 0x60, 0x10, 0x55, 0x1d, 0x68, 0xe0, 0x19, 0x04, 0x98, 0x34, +0x4c, 0x47, 0xe7, 0x44, 0x9d, 0x71, 0x54, 0xd5, 0x6b, 0x20, 0x19, 0xd8, +0xa0, 0x8b, 0xf6, 0xee, 0xc7, 0xc4, 0x9c, 0x68, 0x60, 0x30, 0x8a, 0xaa, +0xf6, 0x34, 0xf0, 0x0c, 0x82, 0x69, 0xd0, 0xab, 0x88, 0xaa, 0xea, 0x9c, +0x0c, 0x82, 0xa9, 0xd0, 0xab, 0x88, 0xaa, 0xea, 0x7c, 0x0c, 0x42, 0xe8, +0xd7, 0xab, 0x88, 0xaa, 0xea, 0x5c, 0x0c, 0x02, 0x06, 0x7a, 0x15, 0x51, +0x55, 0x9d, 0x87, 0x41, 0xc0, 0x41, 0xe1, 0xe8, 0x9c, 0xa8, 0x33, 0x8f, +0xaa, 0x2a, 0x1c, 0xcf, 0x20, 0x60, 0xe1, 0xae, 0x22, 0xaa, 0xea, 0xc0, +0x33, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, +0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, +0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, +0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x2c, 0x83, 0x15, 0x5f, 0xaa, +0xfa, 0x7d, 0x73, 0xff, 0x60, 0x5c, 0xaa, 0x8a, 0x3f, 0xb0, 0x32, 0x77, +0x21, 0x7c, 0xa9, 0xea, 0xc3, 0xfd, 0xd3, 0xd3, 0xdd, 0x96, 0x61, 0xe0, +0xc1, 0x17, 0xbb, 0xf2, 0x2e, 0x84, 0x2f, 0x55, 0xc5, 0xb7, 0xed, 0xdd, +0xe3, 0xe3, 0xb6, 0x24, 0x18, 0x68, 0xf0, 0xc5, 0xae, 0xbc, 0x0b, 0x61, +0x4b, 0x55, 0xbb, 0x4a, 0xd3, 0x72, 0xfb, 0xfc, 0x5c, 0xee, 0x8d, 0x4a, +0x55, 0x43, 0xb1, 0xab, 0xa9, 0x0b, 0xe1, 0x4b, 0x55, 0xbb, 0x4a, 0xd3, +0x7d, 0xd9, 0xf4, 0x91, 0x1a, 0x95, 0xaa, 0x86, 0x62, 0x57, 0x43, 0x17, +0xc2, 0x97, 0xaa, 0xfa, 0x4a, 0xd3, 0xae, 0x8f, 0x74, 0x0a, 0x03, 0x0f, +0xa1, 0x8b, 0x5d, 0x79, 0x17, 0xc2, 0x97, 0xaa, 0x86, 0x4a, 0xd3, 0xa6, +0x8f, 0xd4, 0xe9, 0x78, 0x06, 0x1e, 0x7c, 0xb1, 0x2b, 0xef, 0x42, 0xe8, +0x52, 0xd5, 0x5e, 0xa5, 0xa9, 0xba, 0xa6, 0x8f, 0x74, 0x2c, 0x03, 0x0f, +0xbe, 0xd8, 0x95, 0x77, 0x21, 0x7c, 0xa9, 0x6a, 0xbf, 0xd2, 0x54, 0xb5, +0x2c, 0xe1, 0xc6, 0x31, 0xf0, 0xe0, 0x8b, 0x5d, 0x79, 0x17, 0x42, 0x97, +0xaa, 0x0e, 0x2a, 0x4d, 0x1d, 0x9a, 0x3e, 0xd2, 0x31, 0x0c, 0x3c, 0xf8, +0x62, 0x57, 0xde, 0x85, 0xf0, 0xa5, 0xaa, 0xc3, 0x4a, 0xd3, 0xae, 0x8f, +0x74, 0x04, 0x03, 0x0d, 0xbe, 0xd8, 0x95, 0x77, 0x21, 0x74, 0xa9, 0xea, +0xd1, 0x4a, 0xd3, 0xa6, 0x8f, 0x14, 0xee, 0x5c, 0x06, 0x1e, 0x7c, 0xb1, +0x2b, 0xef, 0x42, 0xe8, 0x52, 0xd5, 0x13, 0x95, 0xa6, 0x0e, 0x4d, 0x1f, +0xe9, 0x79, 0x0c, 0x3c, 0xf8, 0x62, 0x57, 0xde, 0x85, 0xd0, 0xa5, 0xaa, +0x27, 0x2b, 0x4d, 0xdb, 0xa2, 0x42, 0x77, 0x0e, 0x03, 0x0f, 0xbe, 0xd8, +0x95, 0x77, 0x21, 0x7c, 0xa9, 0xea, 0xe9, 0x4a, 0x53, 0x87, 0x46, 0x46, +0x9c, 0x81, 0x07, 0x5f, 0xec, 0xca, 0xbb, 0x10, 0xba, 0x54, 0xf5, 0x44, +0xa5, 0x69, 0x90, 0xe1, 0x5c, 0x8c, 0x81, 0x07, 0x5f, 0xec, 0xca, 0xbb, +0x10, 0xba, 0x54, 0x35, 0x52, 0x69, 0xda, 0x3c, 0x20, 0x31, 0x06, 0x1e, +0x7c, 0xb1, 0x2b, 0xef, 0x42, 0xf8, 0x52, 0xd5, 0x78, 0xa5, 0x69, 0x55, +0x45, 0x18, 0x48, 0x44, 0x25, 0x5c, 0xc4, 0x85, 0xf0, 0xa5, 0xaa, 0xf1, +0x4a, 0xd3, 0x46, 0x46, 0x84, 0x81, 0x44, 0x5c, 0xc2, 0xf2, 0x2e, 0x84, +0x2e, 0x55, 0x3d, 0xb3, 0xd2, 0xb4, 0xaa, 0x4e, 0x32, 0xcc, 0xf5, 0x80, +0x47, 0x24, 0x2c, 0xed, 0x42, 0xe8, 0x52, 0xd5, 0xb3, 0x2b, 0x4d, 0xab, +0x6a, 0xb9, 0x52, 0xd5, 0xb8, 0x84, 0xe5, 0x5d, 0x08, 0x5d, 0xaa, 0x3a, +0xa2, 0xd2, 0xb4, 0xaa, 0x16, 0x2c, 0x55, 0x8d, 0x4b, 0x58, 0xd8, 0x85, +0xd0, 0xa5, 0xaa, 0xa3, 0x2a, 0x4d, 0x55, 0x8f, 0x31, 0x5c, 0xb4, 0x55, +0x75, 0x31, 0x17, 0x42, 0x97, 0xaa, 0x8e, 0xac, 0x34, 0x55, 0x1d, 0x32, +0x5c, 0xb0, 0x55, 0x75, 0x41, 0x17, 0x42, 0x97, 0xaa, 0x8e, 0xae, 0x34, +0x55, 0x7d, 0xcb, 0x60, 0xd0, 0xaa, 0xba, 0x80, 0x0b, 0x99, 0x58, 0xaa, +0x4a, 0x55, 0x9a, 0xaa, 0x7a, 0x06, 0x93, 0x56, 0xd5, 0x85, 0x5c, 0x08, +0x5d, 0xaa, 0x3a, 0xa9, 0xd2, 0x54, 0xb5, 0xc7, 0x60, 0xd0, 0xaa, 0x3a, +0xbf, 0x0b, 0xc1, 0x24, 0x28, 0x5d, 0x69, 0xaa, 0xd6, 0xad, 0xaa, 0xb3, +0xbb, 0x10, 0x4c, 0x84, 0xd2, 0x95, 0xa6, 0x6a, 0xd9, 0xaa, 0xba, 0x80, +0x0b, 0xc1, 0x64, 0x28, 0x5d, 0x69, 0xaa, 0xb6, 0xad, 0xaa, 0x33, 0xbb, +0x10, 0x10, 0x50, 0xba, 0xd2, 0x54, 0x2d, 0x5b, 0x55, 0x67, 0x76, 0x21, +0x74, 0xa9, 0x2a, 0x59, 0x69, 0xaa, 0xea, 0x60, 0xdd, 0xaa, 0xca, 0xbb, +0x68, 0x5c, 0x08, 0x48, 0x38, 0xba, 0xd2, 0xd4, 0xd9, 0xb7, 0xaa, 0xce, +0xe2, 0x62, 0x05, 0x1a, 0x1b, 0xc3, 0xa8, 0x6a, 0xd0, 0xf0, 0x70, 0x0f, +0x63, 0x11, 0x02, 0x16, 0x49, 0x44, 0x55, 0xf9, 0xe3, 0xfb, 0x01, 0xfb, +0x51, 0x26, 0x11, 0x55, 0xe5, 0x8f, 0xef, 0xb7, 0x1f, 0x65, 0x22, 0x51, +0x55, 0xfe, 0xf8, 0xfe, 0x14, 0x46, 0x99, 0x44, 0x54, 0x95, 0x3f, 0xbe, +0x3f, 0x85, 0x51, 0x26, 0x11, 0x55, 0x25, 0x8e, 0xef, 0x4f, 0x68, 0x94, +0x49, 0x44, 0x55, 0xf9, 0xe3, 0xfb, 0x53, 0x18, 0x65, 0x12, 0x51, 0x55, +0xfe, 0xf8, 0xfe, 0x44, 0x46, 0x69, 0x1f, 0x55, 0x65, 0x8f, 0xef, 0x4f, +0x63, 0x94, 0x49, 0x44, 0x55, 0x89, 0xe3, 0xfb, 0xd3, 0x1a, 0xa5, 0x7d, +0x54, 0x95, 0x3f, 0xbe, 0x3f, 0x91, 0x51, 0xda, 0x47, 0x55, 0xf9, 0xe3, +0xfb, 0x13, 0x19, 0xa5, 0x7d, 0x54, 0x95, 0x39, 0xbe, 0x3f, 0xa9, 0x51, +0xda, 0x47, 0x55, 0x89, 0xe3, 0xfb, 0x59, 0x11, 0xf3, 0x8e, 0xd2, 0x3e, +0xaa, 0x4a, 0x1c, 0xdf, 0x9f, 0xd0, 0x28, 0x93, 0x88, 0xaa, 0xf2, 0xc7, +0xf7, 0xa7, 0x30, 0xca, 0x24, 0xa2, 0xaa, 0xdc, 0xf1, 0xfd, 0xa9, 0x8c, +0x32, 0x89, 0xa8, 0x2a, 0x7f, 0x7c, 0x7f, 0x4a, 0xa3, 0x4c, 0x3f, 0xaa, +0x1a, 0xc9, 0xaa, 0xda, 0x8f, 0x32, 0x89, 0xa8, 0x2a, 0x7f, 0x7c, 0x7f, +0x0a, 0xa3, 0x4c, 0x22, 0xaa, 0xca, 0x1f, 0xdf, 0x9f, 0xc8, 0x28, 0xed, +0xa3, 0xaa, 0xfc, 0xf1, 0xfd, 0x89, 0x8c, 0xd2, 0x3e, 0xaa, 0xca, 0x1f, +0xdf, 0x9f, 0xc2, 0x28, 0x7f, 0xc5, 0xa8, 0xaa, 0x22, 0x20, 0xa1, 0xaf, +0x9d, 0x5f, 0x33, 0xaa, 0xaa, 0x7e, 0x90, 0xf6, 0xa3, 0xfc, 0xf5, 0xa3, +0xaa, 0x8a, 0x99, 0x45, 0xc8, 0xc5, 0x73, 0xa2, 0xe9, 0x44, 0x55, 0x35, +0x8d, 0x07, 0xfc, 0x3a, 0xa2, 0xaa, 0x9a, 0xc6, 0xae, 0xbc, 0x8e, 0xa8, +0xaa, 0xa6, 0x31, 0xca, 0xeb, 0x88, 0xaa, 0x6a, 0x1a, 0xa3, 0xbc, 0x8e, +0xa8, 0xaa, 0xa6, 0x32, 0xca, 0x6b, 0x88, 0xaa, 0x6a, 0x1a, 0x5f, 0x3b, +0xd7, 0x11, 0x55, 0x75, 0xa0, 0xf1, 0x3f, 0x19, 0xc2, 0xb4, 0x90, 0x78, +0xb9, 0x76, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, +0x42, 0x60, 0x82, +}; + +#endif diff --git a/src/graphics/moving_texture.cpp b/src/graphics/moving_texture.cpp index 9e8ebc901..bb94203c6 100644 --- a/src/graphics/moving_texture.cpp +++ b/src/graphics/moving_texture.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/moving_texture.hpp b/src/graphics/moving_texture.hpp index f4f7c9870..dbce82e90 100644 --- a/src/graphics/moving_texture.hpp +++ b/src/graphics/moving_texture.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp index 4c44ff8cf..30e8ea1b2 100644 --- a/src/graphics/particle_emitter.cpp +++ b/src/graphics/particle_emitter.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,9 +22,12 @@ #include "graphics/material_manager.hpp" #include "graphics/particle_kind.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/shaders.hpp" +#include "graphics/wind.hpp" #include "io/file_manager.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" +#include "utils/helpers.hpp" #include #include @@ -174,6 +177,46 @@ public: } }; +// ============================================================================ + +class WindAffector : public scene::IParticleAffector +{ + /** (Squared) distance from camera at which a particle is completely faded out */ + float m_speed; + float m_seed; + +public: + WindAffector(float speed): m_speed(speed) + { + m_seed = (float)((rand() % 1000) - 500); + } + + // ------------------------------------------------------------------------ + + virtual void affect(u32 now, scene::SParticle* particlearray, u32 count) + { + const float time = irr_driver->getDevice()->getTimer()->getTime() / 10000.0f; + core::vector3df dir = irr_driver->getWind(); + dir *= m_speed * std::min(noise2d(time, m_seed), -0.2f); + + for (u32 n = 0; n < count; n++) + { + scene::SParticle& cur = particlearray[n]; + + cur.pos += dir; + } // for ngetAbsoluteTransformation(); + core::matrix4 transform = m_node->getAbsoluteTransformation(); core::vector3df velocity(m_particle_type->getVelocityX(), m_particle_type->getVelocityY(), m_particle_type->getVelocityZ()); @@ -489,7 +532,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type) m_node->addAffector(faa); faa->drop(); } - + if (type->hasScaleAffector()) { core::dimension2df factor = core::dimension2df(type->getScaleAffectorFactorX(), @@ -499,6 +542,21 @@ void ParticleEmitter::setParticleType(const ParticleKind* type) m_node->addAffector(scale_affector); scale_affector->drop(); } + + const float windspeed = type->getWindSpeed(); + if (windspeed > 0.01f) + { + WindAffector *waf = new WindAffector(windspeed); + m_node->addAffector(waf); + waf->drop(); + } + + const bool flips = type->getFlips(); + if (flips) + { + m_node->getMaterial(0).MaterialType = irr_driver->getShader(ES_SNOW); + m_node->getMaterial(0).BlendOperation = video::EBO_ADD; + } } } // setParticleType diff --git a/src/graphics/particle_emitter.hpp b/src/graphics/particle_emitter.hpp index e0c4b6b4b..8c3dd941a 100644 --- a/src/graphics/particle_emitter.hpp +++ b/src/graphics/particle_emitter.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/particle_kind.cpp b/src/graphics/particle_kind.cpp index 2d2a46a10..9d4370b18 100644 --- a/src/graphics/particle_kind.cpp +++ b/src/graphics/particle_kind.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -54,6 +54,8 @@ ParticleKind::ParticleKind(const std::string file) : m_min_start_color(255,255,2 m_has_scale_affector = NULL; m_scale_affector_factor_x = 0.0f; m_scale_affector_factor_y = 0.0f; + m_wind_speed = 0; + m_flips = false; // ----- Read XML file @@ -219,6 +221,14 @@ ParticleKind::ParticleKind(const std::string file) : m_min_start_color(255,255,2 m_material_file = material_manager->getLatestMaterial()->getTexFname(); } + // ------------------------------------------------------------------------ + + const XMLNode* wind = xml->getNode("wind"); + if (wind != NULL) + { + wind->get("speed", &m_wind_speed); + wind->get("flips", &m_flips); + } // ------------------------------------------------------------------------ diff --git a/src/graphics/particle_kind.hpp b/src/graphics/particle_kind.hpp index 7d220af86..64c5c8f80 100644 --- a/src/graphics/particle_kind.hpp +++ b/src/graphics/particle_kind.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -86,6 +86,11 @@ private: int m_emission_decay_rate; + /** Wind. < 0.01 if disabled. */ + float m_wind_speed; + + bool m_flips; + std::string m_name; std::string m_material_file; @@ -148,11 +153,14 @@ public: int getEmissionDecayRate() const { return m_emission_decay_rate; } - bool hasScaleAffector() const { return m_has_scale_affector; } float getScaleAffectorFactorX() const { return m_scale_affector_factor_x; } float getScaleAffectorFactorY() const { return m_scale_affector_factor_y; }; + float getWindSpeed() const { return m_wind_speed; } + + bool getFlips() const { return m_flips; } + std::string getName() const { return m_name; } }; diff --git a/src/graphics/particle_kind_manager.cpp b/src/graphics/particle_kind_manager.cpp index 93dee54fe..91ac163f8 100644 --- a/src/graphics/particle_kind_manager.cpp +++ b/src/graphics/particle_kind_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/particle_kind_manager.hpp b/src/graphics/particle_kind_manager.hpp index 9ded19ec2..ef7b7a2f5 100644 --- a/src/graphics/particle_kind_manager.hpp +++ b/src/graphics/particle_kind_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Marianne Gagnon +// Copyright (C) 2011-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/per_camera_node.cpp b/src/graphics/per_camera_node.cpp index 4e6b1d6e7..6bccd7f41 100644 --- a/src/graphics/per_camera_node.cpp +++ b/src/graphics/per_camera_node.cpp @@ -1,6 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Marianne Gagnon -// based on code Copyright (C) 2002-2010 Nikolaus Gebhardt +// Copyright (C) 2011-2013 Marianne Gagnon +// based on code Copyright 2002-2010 Nikolaus Gebhardt // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,7 +24,7 @@ #include PerCameraNode::PerCameraNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, - scene::ICameraSceneNode* camera, scene::IMesh* mesh) + scene::ICameraSceneNode* camera, scene::ISceneNode *node) : IDummyTransformationSceneNode(parent, mgr, id) { #ifdef DEBUG @@ -33,7 +33,10 @@ PerCameraNode::PerCameraNode(scene::ISceneNode* parent, scene::ISceneManager* mg #endif m_camera = camera; - m_child = mgr->addMeshSceneNode(mesh, this); + + node->setParent(this); + m_child = node; + //m_child = mgr->addCubeSceneNode(0.5f, this, -1, core::vector3df(0,0,0), core::vector3df(0,0,0), core::vector3df(3.0f,0.2f,3.0f)); //RelativeTransformationMatrix.setTranslation( core::vector3df(-0.5,-1,3) ); diff --git a/src/graphics/per_camera_node.hpp b/src/graphics/per_camera_node.hpp index fb1799a73..1a7542af6 100644 --- a/src/graphics/per_camera_node.hpp +++ b/src/graphics/per_camera_node.hpp @@ -1,6 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Marianne Gagnon -// based on code Copyright (C) 2002-2010 Nikolaus Gebhardt +// Copyright (C) 2011-2013 Marianne Gagnon +// based on code Copyright 2002-2010 Nikolaus Gebhardt // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -54,7 +54,7 @@ private: public: PerCameraNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, - scene::ICameraSceneNode* camera, scene::IMesh* mesh); + scene::ICameraSceneNode* camera, scene::ISceneNode* node); virtual ~PerCameraNode(); //! returns the axis aligned bounding box of this node diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index d9a0bc6f0..cbe9689ac 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -1,6 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011-2013 the SuperTuxKart team -// Copyright (C) 2013 Joerg Henrichs +// Copyright (C) 2011-2013 the SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -20,69 +19,46 @@ #include "config/user_config.hpp" #include "io/file_manager.hpp" +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/mlaa_areamap.hpp" +#include "graphics/shaders.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/kart_model.hpp" +#include "modes/world.hpp" #include "race/race_manager.hpp" +#include "tracks/track.hpp" #include "utils/log.hpp" -#include -#include - -#define MOTION_BLUR_FACTOR (1.0f/15.0f) -#define MOTION_BLUR_OFFSET 20.0f +#include using namespace video; using namespace scene; -PostProcessing::PostProcessing(video::IVideoDriver* video_driver) +PostProcessing::PostProcessing(IVideoDriver* video_driver) { - // Check if post-processing is supported on this hardware - m_supported = false; - if( irr_driver->isGLSL() ) - { - m_supported = true; - } - - //Check which texture dimensions are supported on this hardware - bool nonsquare = video_driver->queryFeature(video::EVDF_TEXTURE_NSQUARE); - bool nonpower = video_driver->queryFeature(video::EVDF_TEXTURE_NPOT); - if (!nonpower) { - Log::warn("PostProcessing", - "Only power of two textures are supported."); - } - if (!nonsquare) { - Log::warn("PostProcessing", "Only square textures are supported."); - } // Initialization - if(m_supported) + m_material.Wireframe = false; + m_material.Lighting = false; + m_material.ZWriteEnable = false; + m_material.ZBuffer = ECFN_ALWAYS; + m_material.setFlag(EMF_TRILINEAR_FILTER, true); + + for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; ++i) { - // Render target - core::dimension2du opt = video_driver->getScreenSize() - .getOptimalSize(!nonpower, !nonsquare); - m_render_target = - video_driver->addRenderTargetTexture(opt, "postprocess"); - if(!m_render_target) - { - Log::warn("PostProcessing", "Couldn't create the render target " - "for post-processing, disabling it."); - UserConfigParams::m_postprocess_enabled = false; + m_material.TextureLayer[i].TextureWrapU = + m_material.TextureLayer[i].TextureWrapV = ETC_CLAMP_TO_EDGE; } - // Material and shaders - IGPUProgrammingServices* gpu = - video_driver->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "motion_blur.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "motion_blur.frag").c_str(), - "main", video::EPST_PS_2_0, - this, video::EMT_SOLID); - m_blur_material.MaterialType = (E_MATERIAL_TYPE)material_type; - m_blur_material.setTexture(0, m_render_target); - m_blur_material.Wireframe = false; - m_blur_material.Lighting = false; - m_blur_material.ZWriteEnable = false; + // Load the MLAA area map + io::IReadFile *areamap = irr_driver->getDevice()->getFileSystem()-> + createMemoryReadFile((void *) AreaMap33, sizeof(AreaMap33), + "AreaMap33", false); + if (!areamap) Log::fatal("postprocessing", "Failed to load the areamap"); + m_areamap = irr_driver->getVideoDriver()->getTexture(areamap); + areamap->drop(); - } } // PostProcessing // ---------------------------------------------------------------------------- @@ -97,12 +73,15 @@ PostProcessing::~PostProcessing() */ void PostProcessing::reset() { - unsigned int n = Camera::getNumCameras(); + const u32 n = Camera::getNumCameras(); m_boost_time.resize(n); m_vertices.resize(n); m_center.resize(n); m_direction.resize(n); + MotionBlurProvider * const cb = (MotionBlurProvider *) irr_driver-> + getCallback(ES_MOTIONBLUR); + for(unsigned int i=0; isetDirection(i, m_direction[i].X, m_direction[i].Y); + cb->setMaxHeight(i, m_vertices[i].v1.TCoords.Y); } // for i + getCallback(ES_MOTIONBLUR); - bool any_boost = false; - for(unsigned int i=0; i0.0f; + const float tex_height = m_vertices[num].v1.TCoords.Y - m_vertices[num].v0.TCoords.Y; + m_center[num].Y = m_vertices[num].v0.TCoords.Y + y * tex_height; - // Don't capture the input when we have no post-processing to add - // it will be faster and this ay we won't lose anti-aliasing - if(!any_boost) - { - m_used_pp_this_frame = false; - return; - } - - m_used_pp_this_frame = true; - irr_driver->getVideoDriver()->setRenderTarget(m_render_target, true, true); -} // beginCapture + cb->setCenter(num, m_center[num].X, m_center[num].Y); +} // ---------------------------------------------------------------------------- -/** Restore the framebuffer render target. +/** Setup some PP data. */ -void PostProcessing::endCapture() +void PostProcessing::begin() { - if(!m_supported || !UserConfigParams::m_postprocess_enabled || - !m_used_pp_this_frame) - return; - - irr_driver->getVideoDriver()->setRenderTarget(video::ERT_FRAME_BUFFER, - true, true, 0); -} // endCapture + m_any_boost = false; + for (u32 i = 0; i < m_boost_time.size(); i++) + m_any_boost |= m_boost_time[i] > 0.01f; +} // beginCapture // ---------------------------------------------------------------------------- /** Set the boost amount according to the speed of the camera */ void PostProcessing::giveBoost(unsigned int camera_index) { - m_boost_time[camera_index] = 0.75f; + if (irr_driver->isGLSL()) + { + m_boost_time[camera_index] = 0.75f; + + MotionBlurProvider * const cb = (MotionBlurProvider *)irr_driver-> + getCallback(ES_MOTIONBLUR); + cb->setBoostTime(camera_index, m_boost_time[camera_index]); + } } // giveBoost // ---------------------------------------------------------------------------- @@ -204,75 +177,590 @@ void PostProcessing::giveBoost(unsigned int camera_index) */ void PostProcessing::update(float dt) { - for(unsigned int i=0; iisGLSL()) + return; + + MotionBlurProvider* const cb = + (MotionBlurProvider*) irr_driver->getCallback(ES_MOTIONBLUR); + + if (cb == NULL) return; + + for (unsigned int i=0; i 0.0f) { m_boost_time[i] -= dt; if (m_boost_time[i] < 0.0f) m_boost_time[i] = 0.0f; } + + cb->setBoostTime(i, m_boost_time[i]); } } // update +// ---------------------------------------------------------------------------- +/** Render the post-processed scene, solids only, color to color, no stencil */ +void PostProcessing::renderSolid(const u32 cam) +{ + if (!irr_driver->isGLSL()) return; + + // Early out: do nothing if at all possible + if (UserConfigParams::m_ssao < 1 && !World::getWorld()->getTrack()->isFogEnabled()) + return; + + static u8 tick = 0; + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, core::IdentityMatrix); + drv->setTransform(ETS_VIEW, core::IdentityMatrix); + drv->setTransform(ETS_PROJECTION, core::IdentityMatrix); + + GaussianBlurProvider * const gacb = (GaussianBlurProvider *) irr_driver-> + getCallback(ES_GAUSSIAN3H); + + const TypeRTT curssao = tick ? RTT_SSAO2 : RTT_SSAO1; + + if (World::getWorld()->getTrack()->isFogEnabled()) + { + m_material.MaterialType = irr_driver->getShader(ES_FOG); + m_material.setTexture(0, irr_driver->getRTT(RTT_DEPTH)); + + // Overlay + m_material.BlendOperation = EBO_ADD; + m_material.MaterialTypeParam = pack_textureBlendFunc(EBF_SRC_ALPHA, EBF_ONE_MINUS_SRC_ALPHA); + + drv->setRenderTarget(irr_driver->getRTT(RTT_COLOR), false, false); + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + m_material.MaterialTypeParam = 0; + } + + if (UserConfigParams::m_ssao == 1) // SSAO low + { + m_material.MaterialType = irr_driver->getShader(ES_SSAO); + m_material.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + m_material.setTexture(1, irr_driver->getRTT(tick ? RTT_SSAO1 : RTT_SSAO2)); + + drv->setRenderTarget(irr_driver->getRTT(curssao), true, false, + SColor(255, 255, 255, 255)); + + drawQuad(cam, m_material); + + // Blur it to reduce noise. + { + gacb->setResolution(UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + m_material.setTexture(0, irr_driver->getRTT(curssao)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(irr_driver->getRTT(curssao), false, false); + + drawQuad(cam, m_material); + } + + // Overlay + m_material.MaterialType = EMT_ONETEXTURE_BLEND; + m_material.setTexture(0, irr_driver->getRTT(curssao)); + m_material.setTexture(1, 0); + m_material.BlendOperation = EBO_ADD; + m_material.MaterialTypeParam = pack_textureBlendFunc(EBF_DST_COLOR, EBF_ZERO); + + drv->setRenderTarget(irr_driver->getRTT(RTT_COLOR), false, false); + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + m_material.MaterialTypeParam = 0; + + } else if (UserConfigParams::m_ssao == 2) // SSAO high + { + m_material.MaterialType = irr_driver->getShader(ES_SSAO); + m_material.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + m_material.setTexture(1, irr_driver->getRTT(tick ? RTT_SSAO1 : RTT_SSAO2)); + + drv->setRenderTarget(irr_driver->getRTT(curssao), true, false, + SColor(255, 255, 255, 255)); + + drawQuad(cam, m_material); + + // Blur it to reduce noise. + { + gacb->setResolution(UserConfigParams::m_width, + UserConfigParams::m_height); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6V); + m_material.setTexture(0, irr_driver->getRTT(curssao)); + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6H); + m_material.setTexture(0, irr_driver->getRTT(RTT_TMP3)); + drv->setRenderTarget(irr_driver->getRTT(curssao), false, false); + + drawQuad(cam, m_material); + } + + // Overlay + m_material.MaterialType = EMT_ONETEXTURE_BLEND; + m_material.setTexture(0, irr_driver->getRTT(curssao)); + m_material.setTexture(1, 0); + m_material.BlendOperation = EBO_ADD; + m_material.MaterialTypeParam = pack_textureBlendFunc(EBF_DST_COLOR, EBF_ZERO); + + drv->setRenderTarget(irr_driver->getRTT(RTT_COLOR), false, false); + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + m_material.MaterialTypeParam = 0; + } + + tick++; + tick %= 2; +} + // ---------------------------------------------------------------------------- /** Render the post-processed scene */ void PostProcessing::render() { - if(!m_supported || !UserConfigParams::m_postprocess_enabled) - return; + if (!irr_driver->isGLSL()) return; - if (!m_used_pp_this_frame) + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, core::IdentityMatrix); + drv->setTransform(ETS_VIEW, core::IdentityMatrix); + drv->setTransform(ETS_PROJECTION, core::IdentityMatrix); + + MotionBlurProvider * const mocb = (MotionBlurProvider *) irr_driver-> + getCallback(ES_MOTIONBLUR); + GaussianBlurProvider * const gacb = (GaussianBlurProvider *) irr_driver-> + getCallback(ES_GAUSSIAN3H); + + const u32 cams = Camera::getNumCameras(); + for(u32 cam = 0; cam < cams; cam++) { - return; + scene::ICameraSceneNode * const camnode = + Camera::getCamera(cam)->getCameraSceneNode(); + mocb->setCurrentCamera(cam); + ITexture *in = irr_driver->getRTT(RTT_COLOR); + ITexture *out = irr_driver->getRTT(RTT_TMP1); + // Each effect uses these as named, and sets them up for the next effect. + // This allows chaining effects where some may be disabled. + + // As the original color shouldn't be touched, the first effect can't be disabled. + + if (1) // bloom + { + // Blit the base to tmp1 + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, in); + drv->setRenderTarget(out, true, false); + + drawQuad(cam, m_material); + + const bool globalbloom = World::getWorld()->getTrack()->getBloom(); + + BloomPowerProvider * const bloomcb = (BloomPowerProvider *) + irr_driver-> + getCallback(ES_BLOOM_POWER); + + if (globalbloom) + { + const float threshold = World::getWorld()->getTrack()->getBloomThreshold(); + ((BloomProvider *) irr_driver->getCallback(ES_BLOOM))->setThreshold(threshold); + + // Catch bright areas, and progressively minify + m_material.MaterialType = irr_driver->getShader(ES_BLOOM); + m_material.setTexture(0, in); + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + drawQuad(cam, m_material); + } + + // Do we have any forced bloom nodes? If so, draw them now + const std::vector &blooms = irr_driver->getForcedBloom(); + const u32 bloomsize = blooms.size(); + + if (!globalbloom && bloomsize) + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + + if (globalbloom || bloomsize) + { + // Clear the alpha to a suitable value, stencil + glClearColor(0, 0, 0, 0.1f); + glColorMask(0, 0, 0, 1); + + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + glClearColor(0, 0, 0, 0); + glColorMask(1, 1, 1, 1); + + // The forced-bloom objects are drawn again, to know which pixels to pick. + // While it's more drawcalls, there's a cost to using four MRTs over three, + // and there shouldn't be many such objects in a track. + // The stencil is already in use for the glow. The alpha channel is best + // reserved for other use (specular, etc). + // + // They are drawn with depth and color writes off, giving 4x-8x drawing speed. + if (bloomsize) + { + const core::aabbox3df &cambox = camnode-> + getViewFrustum()-> + getBoundingBox(); + + irr_driver->getSceneManager()->setCurrentRendertime(ESNRP_SOLID); + SOverrideMaterial &overridemat = drv->getOverrideMaterial(); + overridemat.EnablePasses = ESNRP_SOLID; + overridemat.EnableFlags = EMF_MATERIAL_TYPE | EMF_ZWRITE_ENABLE | EMF_COLOR_MASK; + overridemat.Enabled = true; + + overridemat.Material.MaterialType = irr_driver->getShader(ES_BLOOM_POWER); + overridemat.Material.ZWriteEnable = false; + overridemat.Material.ColorMask = ECP_ALPHA; + + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, ~0); + glEnable(GL_STENCIL_TEST); + + camnode->render(); + + for (u32 i = 0; i < bloomsize; i++) + { + scene::ISceneNode * const cur = blooms[i].node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + bloomcb->setPower(blooms[i].power); + + cur->render(); + } + + // Second pass for transparents. No-op for solids. + irr_driver->getSceneManager()->setCurrentRendertime(ESNRP_TRANSPARENT); + for (u32 i = 0; i < bloomsize; i++) + { + scene::ISceneNode * const cur = blooms[i].node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + bloomcb->setPower(blooms[i].power); + + cur->render(); + } + + overridemat.Enabled = 0; + overridemat.EnablePasses = 0; + + // Ok, we have the stencil; now use it to blit from color to bloom tex + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, ~0); + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_COLOR)); + + // Just in case. + glColorMask(1, 1, 1, 0); + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), false, false); + + m_material.ColorMask = ECP_RGB; + drawQuad(cam, m_material); + m_material.ColorMask = ECP_ALL; + + glColorMask(1, 1, 1, 1); + glDisable(GL_STENCIL_TEST); + } // end forced bloom + + // To half + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_TMP3)); + drv->setRenderTarget(irr_driver->getRTT(RTT_HALF1), true, false); + + drawQuad(cam, m_material); + + // To quarter + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_HALF1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), true, false); + + drawQuad(cam, m_material); + + // To eighth + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_EIGHTH1), true, false); + + drawQuad(cam, m_material); + + // Blur it for distribution. + { + gacb->setResolution(UserConfigParams::m_width / 8, + UserConfigParams::m_height / 8); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6V); + m_material.setTexture(0, irr_driver->getRTT(RTT_EIGHTH1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_EIGHTH2), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6H); + m_material.setTexture(0, irr_driver->getRTT(RTT_EIGHTH2)); + drv->setRenderTarget(irr_driver->getRTT(RTT_EIGHTH1), false, false); + + drawQuad(cam, m_material); + } + + // Additively blend on top of tmp1 + m_material.BlendOperation = EBO_ADD; + m_material.MaterialType = irr_driver->getShader(ES_BLOOM_BLEND); + m_material.setTexture(0, irr_driver->getRTT(RTT_EIGHTH1)); + drv->setRenderTarget(out, false, false); + + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + } // end if bloom + + in = irr_driver->getRTT(RTT_TMP1); + out = irr_driver->getRTT(RTT_TMP2); + } + + if (World::getWorld()->getTrack()->hasGodRays() && m_sunpixels > 30) // god rays + { + // Grab the sky + drv->setRenderTarget(out, true, false); + irr_driver->getSceneManager()->drawAll(ESNRP_SKY_BOX); + + // Set the sun's color + ColorizeProvider * const colcb = (ColorizeProvider *) irr_driver->getCallback(ES_COLORIZE); + const SColor col = World::getWorld()->getTrack()->getSunColor(); + colcb->setColor(col.getRed() / 255.0f, col.getGreen() / 255.0f, col.getBlue() / 255.0f); + + // The sun interposer + IMeshSceneNode * const sun = irr_driver->getSunInterposer(); + sun->getMaterial(0).ColorMask = ECP_ALL; + irr_driver->getSceneManager()->drawAll(ESNRP_CAMERA); + irr_driver->getSceneManager()->setCurrentRendertime(ESNRP_SOLID); + + sun->render(); + + sun->getMaterial(0).ColorMask = ECP_NONE; + + // Fade to quarter + m_material.MaterialType = irr_driver->getShader(ES_GODFADE); + m_material.setTexture(0, out); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), false, false); + + drawQuad(cam, m_material); + + // Blur + { + gacb->setResolution(UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), false, false); + + drawQuad(cam, m_material); + } + + // Calculate the sun's position in texcoords + const core::vector3df pos = sun->getPosition(); + float ndc[4]; + core::matrix4 trans = camnode->getProjectionMatrix(); + trans *= camnode->getViewMatrix(); + + trans.transformVect(ndc, pos); + + const float texh = m_vertices[cam].v1.TCoords.Y - m_vertices[cam].v0.TCoords.Y; + const float texw = m_vertices[cam].v3.TCoords.X - m_vertices[cam].v0.TCoords.X; + + const float sunx = ((ndc[0] / ndc[3]) * 0.5f + 0.5f) * texw; + const float suny = ((ndc[1] / ndc[3]) * 0.5f + 0.5f) * texh; + + ((GodRayProvider *) irr_driver->getCallback(ES_GODRAY))-> + setSunPosition(sunx, suny); + + // Rays please + m_material.MaterialType = irr_driver->getShader(ES_GODRAY); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), true, false); + + drawQuad(cam, m_material); + + // Blur + { + gacb->setResolution(UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), false, false); + + drawQuad(cam, m_material); + } + + // Overlay + m_material.MaterialType = EMT_TRANSPARENT_ADD_COLOR; + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(in, false, false); + + drawQuad(cam, m_material); + } + + if (UserConfigParams::m_motionblur && m_any_boost) // motion blur + { + // Calculate the kart's Y position on screen + const core::vector3df pos = + Camera::getCamera(cam)->getKart()->getNode()->getPosition(); + float ndc[4]; + core::matrix4 trans = camnode->getProjectionMatrix(); + trans *= camnode->getViewMatrix(); + + trans.transformVect(ndc, pos); + const float karty = (ndc[1] / ndc[3]) * 0.5f + 0.5f; + setMotionBlurCenterY(cam, karty); + + + m_material.MaterialType = irr_driver->getShader(ES_MOTIONBLUR); + m_material.setTexture(0, in); + drv->setRenderTarget(out, true, false); + + drawQuad(cam, m_material); + + ITexture *tmp = in; + in = out; + out = tmp; + } + + if (irr_driver->getDisplacingNodes().size()) // Displacement + { + m_material.MaterialType = irr_driver->getShader(ES_PPDISPLACE); + m_material.setFlag(EMF_BILINEAR_FILTER, false); + m_material.setTexture(0, in); + m_material.setTexture(1, irr_driver->getRTT(RTT_DISPLACE)); + drv->setRenderTarget(out, true, false); + + drawQuad(cam, m_material); + + m_material.setTexture(1, 0); + m_material.setFlag(EMF_BILINEAR_FILTER, true); + + ITexture *tmp = in; + in = out; + out = tmp; + } + + if (UserConfigParams::m_mlaa) // MLAA. Must be the last pp filter. + { + drv->setRenderTarget(out, false, false); + + glEnable(GL_STENCIL_TEST); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glStencilFunc(GL_ALWAYS, 1, ~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + // Pass 1: color edge detection + m_material.setFlag(EMF_BILINEAR_FILTER, false); + m_material.setFlag(EMF_TRILINEAR_FILTER, false); + m_material.MaterialType = irr_driver->getShader(ES_MLAA_COLOR1); + m_material.setTexture(0, in); + + drawQuad(cam, m_material); + m_material.setFlag(EMF_BILINEAR_FILTER, true); + m_material.setFlag(EMF_TRILINEAR_FILTER, true); + + glStencilFunc(GL_EQUAL, 1, ~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + // Pass 2: blend weights + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + m_material.MaterialType = irr_driver->getShader(ES_MLAA_BLEND2); + m_material.setTexture(0, out); + m_material.setTexture(1, m_areamap); + m_material.TextureLayer[1].BilinearFilter = false; + m_material.TextureLayer[1].TrilinearFilter = false; + + drawQuad(cam, m_material); + + m_material.TextureLayer[1].BilinearFilter = true; + m_material.TextureLayer[1].TrilinearFilter = true; + m_material.setTexture(1, 0); + + // Pass 3: gather + drv->setRenderTarget(in, false, false); + + m_material.setFlag(EMF_BILINEAR_FILTER, false); + m_material.setFlag(EMF_TRILINEAR_FILTER, false); + m_material.MaterialType = irr_driver->getShader(ES_MLAA_NEIGH3); + m_material.setTexture(0, irr_driver->getRTT(RTT_TMP3)); + m_material.setTexture(1, irr_driver->getRTT(RTT_COLOR)); + + drawQuad(cam, m_material); + + m_material.setFlag(EMF_BILINEAR_FILTER, true); + m_material.setFlag(EMF_TRILINEAR_FILTER, true); + m_material.setTexture(1, 0); + + // Done. + glDisable(GL_STENCIL_TEST); + } + + // Final blit + + if (irr_driver->getNormals()) + { + m_material.MaterialType = irr_driver->getShader(ES_FLIP); + m_material.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + } else if (irr_driver->getSSAOViz()) + { + m_material.MaterialType = irr_driver->getShader(ES_FLIP); + m_material.setTexture(0, irr_driver->getRTT(RTT_SSAO1)); + } else if (irr_driver->getShadowViz()) + { + m_material.MaterialType = irr_driver->getShader(ES_FLIP); + m_material.setTexture(0, irr_driver->getRTT(RTT_SHADOW)); + } else + { + m_material.MaterialType = irr_driver->getShader(ES_COLOR_LEVELS); + m_material.setTexture(0, in); + } + + drv->setRenderTarget(ERT_FRAME_BUFFER, false, false); + + drawQuad(cam, m_material); } - - u16 indices[6] = {0, 1, 2, 3, 0, 2}; - - for(m_current_camera=0; m_current_cameragetVideoDriver(); - video_driver->setMaterial(m_blur_material); - video_driver->drawIndexedTriangleList(&(m_vertices[m_current_camera].v0), - 4, &indices[0], 2); - } - } // render -// ---------------------------------------------------------------------------- -/** Implement IShaderConstantsSetCallback. Shader constants setter for - * post-processing */ -void PostProcessing::OnSetConstants(video::IMaterialRendererServices *services, - s32 user_data) +void PostProcessing::drawQuad(u32 cam, const SMaterial &mat) { - // We need the maximum texture coordinates: - float max_tex_height = m_vertices[m_current_camera].v1.TCoords.Y; - services->setPixelShaderConstant("max_tex_height", &max_tex_height, 1); + const u16 indices[6] = {0, 1, 2, 3, 0, 2}; + IVideoDriver * const drv = irr_driver->getVideoDriver(); - // Scale the boost time to get a usable boost amount: - float boost_amount = m_boost_time[m_current_camera] * 0.7f; + drv->setTransform(ETS_WORLD, core::IdentityMatrix); + drv->setTransform(ETS_VIEW, core::IdentityMatrix); + drv->setTransform(ETS_PROJECTION, core::IdentityMatrix); - // Especially for single screen the top of the screen is less blurred - // in the fragment shader by multiplying the blurr factor by - // (max_tex_height - texcoords.t), where max_tex_height is the maximum - // texture coordinate (1.0 or 0.5). In split screen this factor is too - // small (half the value compared with non-split screen), so we - // multiply this by 2. - if(m_boost_time.size()>1) - boost_amount *= 2.0f; - - services->setPixelShaderConstant("boost_amount", &boost_amount, 1); - services->setPixelShaderConstant("center", - &(m_center[m_current_camera].X), 2); - services->setPixelShaderConstant("direction", - &(m_direction[m_current_camera].X), 2); - - // Use a radius of 0.15 when showing a single kart, otherwise (2-4 karts - // on splitscreen) use only 0.75. - float radius = Camera::getNumCameras()==1 ? 0.15f : 0.075f; - services->setPixelShaderConstant("mask_radius", &radius, 1); - const int texunit = 0; - services->setPixelShaderConstant("color_buffer", &texunit, 1); -} // OnSetConstants + drv->setMaterial(mat); + drv->drawIndexedTriangleList(&(m_vertices[cam].v0), + 4, indices, 2); +} diff --git a/src/graphics/post_processing.hpp b/src/graphics/post_processing.hpp index 136c0e1ec..020cb3ffc 100644 --- a/src/graphics/post_processing.hpp +++ b/src/graphics/post_processing.hpp @@ -1,6 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011-2013 the SuperTuxKart team -// Copyright (C) 2013 Joerg Henrichs +// Copyright (C) 2011-2013 the SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,61 +33,58 @@ using namespace irr; /** \brief Handles post processing, eg motion blur * \ingroup graphics */ -class PostProcessing : public video::IShaderConstantSetCallBack +class PostProcessing: public IReferenceCounted { private: - video::ITexture *m_render_target; - /** Material to be used when blurring is used. */ - video::SMaterial m_blur_material; - - bool m_supported; + video::SMaterial m_material; /** Boost time, how long the boost should be displayed. This also * affects the strength of the effect: longer boost time will * have a stronger effect. */ std::vector m_boost_time; + bool m_any_boost; + /** The center of blurring, in texture coordinates [0,1]).*/ std::vector m_center; /** The center to which the blurring is aimed at, in [0,1]. */ std::vector m_direction; - /** True if any of the cameras is using post processing. */ - bool m_used_pp_this_frame; - - /** Currently active camera during post-processing, needed in the - * OnSetConstants callback. */ - unsigned int m_current_camera; - - struct Quad { video::S3DVertex v0, v1, v2, v3; }; /** The vertices for the rectangle used for each camera. This includes * the vertex position, normal, and texture coordinate. */ std::vector m_vertices; + video::ITexture *m_areamap; + + u32 m_sunpixels; + + void setMotionBlurCenterY(const u32 num, const float y); + public: PostProcessing(video::IVideoDriver* video_driver); virtual ~PostProcessing(); void reset(); /** Those should be called around the part where we render the scene to be post-processed */ - void beginCapture(); - void endCapture(); + void begin(); void update(float dt); + /** Render the post-processed scene, solids only, color to color, no stencil */ + void renderSolid(const u32 cam); + /** Render the post-processed scene */ void render(); - /** Is the hardware able to use post-processing? */ - inline bool isSupported() const {return m_supported;} + /** Draw the quad for this camera */ + void drawQuad(u32 cam, const video::SMaterial &mat); /** Use motion blur for a short time */ void giveBoost(unsigned int cam_index); - /** Implement IShaderConstantsSetCallback. Shader constants setter for post-processing */ - virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 user_data); + void setSunPixels(const u32 in) { m_sunpixels = in; } }; #endif // HEADER_POST_PROCESSING_HPP diff --git a/src/graphics/rain.cpp b/src/graphics/rain.cpp index 4edd7d9a2..adfe7c0cd 100644 --- a/src/graphics/rain.cpp +++ b/src/graphics/rain.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -18,28 +18,117 @@ #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" +#include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" #include "graphics/material_manager.hpp" #include "graphics/material.hpp" #include "graphics/per_camera_node.hpp" #include "graphics/rain.hpp" +#include "graphics/shaders.hpp" #include "modes/world.hpp" #include "states_screens/race_gui.hpp" #include "utils/constants.hpp" #include "utils/random_generator.hpp" +#include #include -#include -const float RAIN_RADIUS[RAIN_RING_COUNT] = { 1.0f, 3.0f, 6.0f, 12.0f, 24.0f }; -const float RAIN_Y_TO = 25.0f; -const float RAIN_Y_FROM = -10.0f; -const float RAIN_DY = 2.5f; -const float RAIN_DX = 0.0f; +using namespace video; +using namespace scene; +using namespace core; -const float TEXTURE_X_TILES[RAIN_RING_COUNT] = { 2.0f, 2.5f, 3.5f, 5.0f, 8.0f }; -const float TEXTURE_Y_TILES[RAIN_RING_COUNT] = { 8.0f, 7.0f, 6.0f, 4.0f, 4.0f }; +// The actual rain node +class RainNode: public scene::ISceneNode +{ +public: + RainNode(scene::ISceneManager* mgr, ITexture *tex) + : scene::ISceneNode(0, mgr, -1) + { + mat.Lighting = false; + mat.ZWriteEnable = false; + mat.MaterialType = irr_driver->getShader(ES_RAIN); + mat.Thickness = 200; + mat.BlendOperation = EBO_ADD; + mat.setTexture(0, tex); + mat.TextureLayer[0].TextureWrapU = + mat.TextureLayer[0].TextureWrapV = ETC_CLAMP_TO_EDGE; + + count = 2500; + area = 3500; + + // Fill in the mesh buffer + buf.Vertices.clear(); + buf.Indices.clear(); + + buf.Vertices.set_used(count); + buf.Indices.set_used(count); + + buf.Primitive = EPT_POINT_SPRITES; + buf.setHardwareMappingHint(EHM_STATIC); + + u32 i; + float x, y, z; + for (i = 0; i < count; i++) + { + x = ((rand() % area) - area/2) / 100.0f; + y = ((rand() % 2400)) / 100.0f; + z = ((rand() % area) - area/2) / 100.0f; + + buf.Indices[i] = i; + buf.Vertices[i] = S3DVertex(x, y, z, 0, 0, 0, SColor(255, 255, 0, 0), 0, 0); + } + + box.addInternalPoint(vector3df((float)(-area/2))); + box.addInternalPoint(vector3df((float)( area/2))); + } + + ~RainNode() + { + } + + virtual void render() + { + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(mat); + + drv->drawMeshBuffer(&buf); + + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + } + + virtual const core::aabbox3d& getBoundingBox() const + { + return box; + } + + virtual void OnRegisterSceneNode() + { + if (IsVisible && + (irr_driver->getRenderPass() & ESNRP_TRANSPARENT) == ESNRP_TRANSPARENT) + { + SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT); + } + + ISceneNode::OnRegisterSceneNode(); + } + + virtual u32 getMaterialCount() const { return 1; } + virtual video::SMaterial& getMaterial(u32 i) { return mat; } + +private: + video::SMaterial mat; + core::aabbox3d box; + u32 count; + s32 area; + + scene::SMeshBuffer buf; +}; + +// The rain manager Rain::Rain(Camera *camera, irr::scene::ISceneNode* parent) { @@ -54,80 +143,17 @@ Rain::Rain(Camera *camera, irr::scene::ISceneNode* parent) RandomGenerator g; m_next_lightning = (float)g.get(35); - for (int r=0; rMaterial.setTexture(0, m->getTexture()); - m->setMaterialProperties(&buffer->Material, NULL); - buffer->Material.ZWriteEnable = false; - buffer->Material.BackfaceCulling = false; - - m_materials.push_back(&buffer->Material); - - video::S3DVertex v; - v.Color.set(255,255,255,255); - - // create a cylinder mesh - const int VERTICES = 17; - - for (int vid=0; vidVertices.push_back(v); - - v.Pos.Y = RAIN_Y_FROM; - - v.TCoords.Y = 0.0f; - buffer->Vertices.push_back(v); - - if (vid > 0) - { - buffer->Indices.push_back(vid-2); - buffer->Indices.push_back(vid-1); - buffer->Indices.push_back(vid); - buffer->Indices.push_back(vid-1); - buffer->Indices.push_back(vid); - buffer->Indices.push_back(vid+1); - } - } - - scene::SMesh* mesh = new scene::SMesh(); - mesh->addMeshBuffer(buffer); - mesh->recalculateBoundingBox(); - - m_node[r] = irr_driver->addPerCameraMesh(mesh, - camera->getCameraSceneNode(), - parent); - m_node[r]->setAutomaticCulling(0); - mesh->drop(); - - buffer->drop(); - } + RainNode *node = new RainNode(irr_driver->getSceneManager(), m->getTexture()); + m_node = irr_driver->addPerCameraNode(node, camera->getCameraSceneNode(), parent); + m_node->setAutomaticCulling(0); } // Rain // ---------------------------------------------------------------------------- Rain::~Rain() { - for (int r=0; rdrop(); // drop STK's reference - m_node[r]->remove(); // Then remove it from the scene graph. - } + m_node->drop(); // drop STK's reference + m_node->remove(); // Then remove it from the scene graph. if (m_lightning && m_thunder_sound != NULL) sfx_manager->deleteSFX(m_thunder_sound); } @@ -136,19 +162,6 @@ Rain::~Rain() void Rain::update(float dt) { - //const int count = m_materials.size(); - for (int m=0; m 1.0f) m_x[m] = fmod(m_x[m], 1.0f); - if (m_y[m] > 1.0f) m_y[m] = fmod(m_y[m], 1.0f); - - core::matrix4& matrix = m_node[m]->getChild()->getMaterial(0).getTextureMatrix(0); - - matrix.setTextureTranslate(m_x[m], m_y[m]); - } - if (m_lightning) { m_next_lightning -= dt; @@ -173,15 +186,12 @@ void Rain::update(float dt) void Rain::setPosition(const core::vector3df& position) { - for (int m=0; mgetChild()->setPosition(position); - } + m_node->getChild()->setPosition(position); } // setPosition // ---------------------------------------------------------------------------- void Rain::setCamera(scene::ICameraSceneNode* camera) { - for (int n=0; nsetCamera(camera); + m_node->setCamera(camera); } diff --git a/src/graphics/rain.hpp b/src/graphics/rain.hpp index 4f7f13f24..b8408c2a8 100644 --- a/src/graphics/rain.hpp +++ b/src/graphics/rain.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,23 +25,16 @@ class PerCameraNode; #include namespace irr { - namespace video { class SMaterial; } + namespace video { class SMaterial; class ITexture; } namespace scene { class ICameraSceneNode; class ISceneNode; } } using namespace irr; -const int RAIN_RING_COUNT = 5; - class SFXBase; class Rain { - PerCameraNode* m_node[RAIN_RING_COUNT]; - - std::vector m_materials; - - float m_x[RAIN_RING_COUNT]; - float m_y[RAIN_RING_COUNT]; + PerCameraNode* m_node; float m_next_lightning; bool m_lightning; @@ -49,7 +42,7 @@ class Rain public: Rain(Camera* camera, irr::scene::ISceneNode* parent); - ~Rain(); + virtual ~Rain(); void update(float dt); void setPosition(const irr::core::vector3df& position); diff --git a/src/graphics/referee.cpp b/src/graphics/referee.cpp index 9f9d45520..4579991cc 100644 --- a/src/graphics/referee.cpp +++ b/src/graphics/referee.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -124,6 +124,8 @@ void Referee::init() scene::IMeshBuffer *mb = m_st_referee_mesh->getMeshBuffer(i); video::SMaterial &irrMaterial = mb->getMaterial(); video::ITexture* t=irrMaterial.getTexture(0); + if(!t) continue; + std::string name=StringUtils::getBasename(t->getName() .getInternalName().c_str()); if(name==colors[0] || name==colors[1] ||name==colors[2] ) @@ -171,6 +173,8 @@ Referee::Referee() m_scene_node->setScale(m_st_scale.toIrrVector()); m_scene_node->setFrameLoop(m_st_first_start_frame, m_st_last_start_frame); + + irr_driver->applyObjectPassShader(m_scene_node); } // Referee // ---------------------------------------------------------------------------- @@ -195,6 +199,8 @@ Referee::Referee(const AbstractKart &kart) m_scene_node->setPosition(core::vector3df(0, kart.getKartHeight() + 0.4f, 0)); m_scene_node->setFrameLoop(m_st_first_rescue_frame, m_st_last_rescue_frame); + + irr_driver->applyObjectPassShader(m_scene_node); } // Referee // ---------------------------------------------------------------------------- diff --git a/src/graphics/referee.hpp b/src/graphics/referee.hpp index 1aca693ee..93941f051 100644 --- a/src/graphics/referee.hpp +++ b/src/graphics/referee.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,7 +19,10 @@ #ifndef HEADER_REFEREE_HPP #define HEADER_REFEREE_HPP -#include "irrlicht.h" +#include +#include +#include +#include using namespace irr; #include "utils/vec3.hpp" diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp new file mode 100644 index 000000000..1a412e1d0 --- /dev/null +++ b/src/graphics/render.cpp @@ -0,0 +1,836 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009 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 "graphics/irr_driver.hpp" + +#include "config/user_config.hpp" +#include "graphics/callbacks.hpp" +#include "graphics/camera.hpp" +#include "graphics/glow.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/lens_flare.hpp" +#include "graphics/light.hpp" +#include "graphics/lod_node.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/particle_kind_manager.hpp" +#include "graphics/per_camera_node.hpp" +#include "graphics/post_processing.hpp" +#include "graphics/referee.hpp" +#include "graphics/rtts.hpp" +#include "graphics/screenquad.hpp" +#include "graphics/shaders.hpp" +#include "graphics/shadow_importance.hpp" +#include "graphics/wind.hpp" +#include "io/file_manager.hpp" +#include "items/item.hpp" +#include "items/item_manager.hpp" +#include "modes/world.hpp" +#include "physics/physics.hpp" +#include "tracks/track.hpp" +#include "utils/constants.hpp" +#include "utils/helpers.hpp" +#include "utils/log.hpp" +#include "utils/profiler.hpp" + +void IrrDriver::renderGLSL(float dt) +{ + World *world = World::getWorld(); // Never NULL. + + // Overrides + video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial(); + overridemat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT; + overridemat.EnableFlags = 0; + + if (m_wireframe) + { + overridemat.Material.Wireframe = 1; + overridemat.EnableFlags |= video::EMF_WIREFRAME; + } + if (m_mipviz) + { + overridemat.Material.MaterialType = m_shaders->getShader(ES_MIPVIZ); + overridemat.EnableFlags |= video::EMF_MATERIAL_TYPE; + overridemat.EnablePasses = scene::ESNRP_SOLID; + } + + // Get a list of all glowing things. The driver's list contains the static ones, + // here we add items, as they may disappear each frame. + std::vector glows = m_glowing; + std::vector transparent_glow_nodes; + + ItemManager * const items = ItemManager::get(); + const u32 itemcount = items->getNumberOfItems(); + u32 i; + + // For each static node, give it a glow representation + const u32 staticglows = glows.size(); + for (i = 0; i < staticglows; i++) + { + scene::ISceneNode * const node = glows[i].node; + + const float radius = (node->getBoundingBox().getExtent().getLength() / 2) * 2.0f; + GlowNode * const repnode = new GlowNode(irr_driver->getSceneManager(), radius); + repnode->setPosition(node->getTransformedBoundingBox().getCenter()); + transparent_glow_nodes.push_back(repnode); + } + + for (i = 0; i < itemcount; i++) + { + Item * const item = items->getItem(i); + if (!item) continue; + const Item::ItemType type = item->getType(); + + if (type != Item::ITEM_NITRO_BIG && type != Item::ITEM_NITRO_SMALL && + type != Item::ITEM_BONUS_BOX && type != Item::ITEM_BANANA && type != Item::ITEM_BUBBLEGUM) + continue; + + LODNode * const lod = (LODNode *) item->getSceneNode(); + if (!lod->isVisible()) continue; + + const int level = lod->getLevel(); + if (level < 0) continue; + + scene::ISceneNode * const node = lod->getAllNodes()[level]; + node->updateAbsolutePosition(); + + GlowData dat; + dat.node = node; + + dat.r = 1.0f; + dat.g = 1.0f; + dat.b = 1.0f; + + // Item colors + switch (type) + { + case Item::ITEM_NITRO_BIG: + case Item::ITEM_NITRO_SMALL: + dat.r = stk_config->m_nitro_glow_color[0]; + dat.g = stk_config->m_nitro_glow_color[1]; + dat.b = stk_config->m_nitro_glow_color[2]; + break; + case Item::ITEM_BONUS_BOX: + dat.r = stk_config->m_box_glow_color[0]; + dat.g = stk_config->m_box_glow_color[1]; + dat.b = stk_config->m_box_glow_color[2]; + break; + case Item::ITEM_BANANA: + dat.r = stk_config->m_banana_glow_color[0]; + dat.g = stk_config->m_banana_glow_color[1]; + dat.b = stk_config->m_banana_glow_color[2]; + break; + case Item::ITEM_BUBBLEGUM: + dat.r = stk_config->m_bubblegum_glow_color[0]; + dat.g = stk_config->m_bubblegum_glow_color[1]; + dat.b = stk_config->m_bubblegum_glow_color[2]; + break; + default: + Log::fatal("render", "Unknown item type got through"); + break; + } + + glows.push_back(dat); + + // Push back its representation too + const float radius = (node->getBoundingBox().getExtent().getLength() / 2) * 2.0f; + GlowNode * const repnode = new GlowNode(irr_driver->getSceneManager(), radius); + repnode->setPosition(node->getTransformedBoundingBox().getCenter()); + transparent_glow_nodes.push_back(repnode); + } + + // Start the RTT for post-processing. + // We do this before beginScene() because we want to capture the glClear() + // because of tracks that do not have skyboxes (generally add-on tracks) + m_post_processing->begin(); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + + m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, + world->getClearColor()); + + // Clear normal and depth to zero + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_NORMAL), true, false, video::SColor(0,0,0,0)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_DEPTH), true, false, video::SColor(0,0,0,0)); + + irr_driver->getVideoDriver()->enableMaterial2D(); + RaceGUIBase *rg = world->getRaceGUI(); + if (rg) rg->update(dt); + + + for(unsigned int cam = 0; cam < Camera::getNumCameras(); cam++) + { + Camera * const camera = Camera::getCamera(cam); + scene::ICameraSceneNode * const camnode = camera->getCameraSceneNode(); + +#ifdef ENABLE_PROFILER + std::ostringstream oss; + oss << "drawAll() for kart " << cam << std::flush; + PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60, + 0x00, 0x00); +#endif + camera->activate(); + rg->preRenderCallback(camera); // adjusts start referee + + const u32 bgnodes = m_background.size(); + if (bgnodes) + { + // If there are background nodes (3d skybox), draw them now. + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + + m_renderpass = scene::ESNRP_SKY_BOX; + m_scene_manager->drawAll(m_renderpass); + + const video::SOverrideMaterial prev = overridemat; + overridemat.Enabled = 1; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE; + overridemat.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + + for (i = 0; i < bgnodes; i++) + { + m_background[i]->setPosition(camnode->getPosition() * 0.97f); + m_background[i]->updateAbsolutePosition(); + m_background[i]->render(); + } + + overridemat = prev; + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, true); + } + + // Fire up the MRT + m_video_driver->setRenderTarget(m_mrt, false, false); + + m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_SOLID; + m_scene_manager->drawAll(m_renderpass); + + ShadowImportanceProvider * const sicb = (ShadowImportanceProvider *) + irr_driver->getCallback(ES_SHADOW_IMPORTANCE); + sicb->updateIPVMatrix(); + + // Used to cull glowing items & lights + const core::aabbox3df cambox = camnode->getViewFrustum()->getBoundingBox(); + + // Render anything glowing. + if (!m_mipviz && !m_wireframe) + { + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_TMP1), false, false); + glClearColor(0, 0, 0, 0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + const u32 glowcount = glows.size(); + ColorizeProvider * const cb = (ColorizeProvider *) m_shaders->m_callbacks[ES_COLORIZE]; + + GlowProvider * const glowcb = (GlowProvider *) m_shaders->m_callbacks[ES_GLOW]; + glowcb->setResolution(UserConfigParams::m_width, + UserConfigParams::m_height); + + overridemat.Material.MaterialType = m_shaders->getShader(ES_COLORIZE); + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE; + overridemat.EnablePasses = scene::ESNRP_SOLID; + overridemat.Enabled = true; + + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, ~0); + glEnable(GL_STENCIL_TEST); + + for (u32 i = 0; i < glowcount; i++) + { + const GlowData &dat = glows[i]; + scene::ISceneNode * const cur = dat.node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + cb->setColor(dat.r, dat.g, dat.b); + cur->render(); + } + + // Second round for transparents; it's a no-op for solids + m_scene_manager->setCurrentRendertime(scene::ESNRP_TRANSPARENT); + overridemat.Material.MaterialType = m_shaders->getShader(ES_COLORIZE_REF); + for (u32 i = 0; i < glowcount; i++) + { + const GlowData &dat = glows[i]; + scene::ISceneNode * const cur = dat.node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + cb->setColor(dat.r, dat.g, dat.b); + cur->render(); + } + overridemat.Enabled = false; + overridemat.EnablePasses = 0; + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDisable(GL_STENCIL_TEST); + + // Cool, now we have the colors set up. Progressively minify. + video::SMaterial minimat; + minimat.Lighting = false; + minimat.ZWriteEnable = false; + minimat.ZBuffer = video::ECFN_ALWAYS; + minimat.setFlag(video::EMF_TRILINEAR_FILTER, true); + + minimat.TextureLayer[0].TextureWrapU = + minimat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + + // To half + minimat.setTexture(0, m_rtts->getRTT(RTT_TMP1)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_HALF1), false, false); + m_post_processing->drawQuad(cam, minimat); + + // To quarter + minimat.setTexture(0, m_rtts->getRTT(RTT_HALF1)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_QUARTER1), false, false); + m_post_processing->drawQuad(cam, minimat); + + // Blur it + ((GaussianBlurProvider *) m_shaders->m_callbacks[ES_GAUSSIAN3H])->setResolution( + UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN6H); + minimat.setTexture(0, m_rtts->getRTT(RTT_QUARTER1)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_QUARTER2), false, false); + m_post_processing->drawQuad(cam, minimat); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN6V); + minimat.setTexture(0, m_rtts->getRTT(RTT_QUARTER2)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_QUARTER1), false, false); + m_post_processing->drawQuad(cam, minimat); + + // The glows will be rendered in the transparent phase + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDisable(GL_STENCIL_TEST); + } // end glow + + // Shadows + if (!m_mipviz && !m_wireframe && UserConfigParams::m_shadows && + World::getWorld()->getTrack()->hasShadows()) + { + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + static u8 tick = 0; + + const Vec3 *vmin, *vmax; + World::getWorld()->getTrack()->getAABB(&vmin, &vmax); + core::aabbox3df trackbox(vmin->toIrrVector(), vmax->toIrrVector() - + core::vector3df(0, 30, 0)); + + const float oldfar = camnode->getFarValue(); + camnode->setFarValue(std::min(100.0f, oldfar)); + camnode->render(); + const core::aabbox3df smallcambox = camnode-> + getViewFrustum()->getBoundingBox(); + camnode->setFarValue(oldfar); + camnode->render(); + + // Set up a nice ortho projection that contains our camera frustum + core::matrix4 ortho; + core::aabbox3df box = smallcambox; + box = box.intersect(trackbox); + + m_suncam->getViewMatrix().transformBoxEx(box); + m_suncam->getViewMatrix().transformBoxEx(trackbox); + + core::vector3df extent = trackbox.getExtent(); + const float w = fabsf(extent.X); + const float h = fabsf(extent.Y); + float z = box.MaxEdge.Z; + + // Snap to texels + const float units_per_w = w / m_rtts->getRTT(RTT_SHADOW)->getSize().Width; + const float units_per_h = h / m_rtts->getRTT(RTT_SHADOW)->getSize().Height; + + float left = box.MinEdge.X; + float right = box.MaxEdge.X; + float up = box.MaxEdge.Y; + float down = box.MinEdge.Y; + + left -= fmodf(left, units_per_w); + right -= fmodf(right, units_per_w); + up -= fmodf(up, units_per_h); + down -= fmodf(down, units_per_h); + z -= fmodf(z, 0.5f); + + // FIXME: quick and dirt (and wrong) workaround to avoid division by zero + if (left == right) right += 0.1f; + if (up == down) down += 0.1f; + if (z == 30) z += 0.1f; + + ortho.buildProjectionMatrixOrthoLH(left, right, + up, down, + 30, z); + + m_suncam->setProjectionMatrix(ortho, true); + m_scene_manager->setActiveCamera(m_suncam); + m_suncam->render(); + + ortho *= m_suncam->getViewMatrix(); + ((SunLightProvider *) m_shaders->m_callbacks[ES_SUNLIGHT])->setShadowMatrix(ortho); + sicb->setShadowMatrix(ortho); + + overridemat.Enabled = 0; + + // Render the importance map + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLLAPSE), true, true); + + m_shadow_importance->render(); + + CollapseProvider * const colcb = (CollapseProvider *) + m_shaders-> + m_callbacks[ES_COLLAPSE]; + ScreenQuad sq(m_video_driver); + sq.setMaterialType(m_shaders->getShader(ES_COLLAPSE)); + sq.setTexture(m_rtts->getRTT(RTT_COLLAPSE)); + sq.getMaterial().setFlag(EMF_BILINEAR_FILTER, false); + + const TypeRTT oldh = tick ? RTT_COLLAPSEH : RTT_COLLAPSEH2; + const TypeRTT oldv = tick ? RTT_COLLAPSEV : RTT_COLLAPSEV2; + const TypeRTT curh = tick ? RTT_COLLAPSEH2 : RTT_COLLAPSEH; + const TypeRTT curv = tick ? RTT_COLLAPSEV2 : RTT_COLLAPSEV; + + colcb->setResolution(1, m_rtts->getRTT(RTT_WARPV)->getSize().Height); + sq.setTexture(m_rtts->getRTT(oldh), 1); + sq.render(m_rtts->getRTT(RTT_WARPH)); + + colcb->setResolution(m_rtts->getRTT(RTT_WARPV)->getSize().Height, 1); + sq.setTexture(m_rtts->getRTT(oldv), 1); + sq.render(m_rtts->getRTT(RTT_WARPV)); + + sq.setTexture(0, 1); + ((GaussianBlurProvider *) m_shaders->m_callbacks[ES_GAUSSIAN3H])->setResolution( + m_rtts->getRTT(RTT_WARPV)->getSize().Height, + m_rtts->getRTT(RTT_WARPV)->getSize().Height); + + sq.setMaterialType(m_shaders->getShader(ES_GAUSSIAN6H)); + sq.setTexture(m_rtts->getRTT(RTT_WARPH)); + sq.render(m_rtts->getRTT(curh)); + + sq.setMaterialType(m_shaders->getShader(ES_GAUSSIAN6V)); + sq.setTexture(m_rtts->getRTT(RTT_WARPV)); + sq.render(m_rtts->getRTT(curv)); + + // Convert importance maps to warp maps + // + // It should be noted that while they do repeated work + // calculating the min, max, and total, it's several hundred us + // faster to do that than to do it once in a separate shader + // (shader switch overhead, measured). + colcb->setResolution(m_rtts->getRTT(RTT_WARPV)->getSize().Height, + m_rtts->getRTT(RTT_WARPV)->getSize().Height); + + sq.setMaterialType(m_shaders->getShader(ES_SHADOW_WARPH)); + sq.setTexture(m_rtts->getRTT(curh)); + sq.render(m_rtts->getRTT(RTT_WARPH)); + + sq.setMaterialType(m_shaders->getShader(ES_SHADOW_WARPV)); + sq.setTexture(m_rtts->getRTT(curv)); + sq.render(m_rtts->getRTT(RTT_WARPV)); + + // Actual shadow map + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_SHADOW), true, true); + overridemat.Material.MaterialType = m_shaders->getShader(ES_SHADOWPASS); + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE | video::EMF_TEXTURE1 | + video::EMF_TEXTURE2; + overridemat.EnablePasses = scene::ESNRP_SOLID; + overridemat.Material.setTexture(1, m_rtts->getRTT(RTT_WARPH)); + overridemat.Material.setTexture(2, m_rtts->getRTT(RTT_WARPV)); + overridemat.Material.TextureLayer[1].TextureWrapU = + overridemat.Material.TextureLayer[1].TextureWrapV = + overridemat.Material.TextureLayer[2].TextureWrapU = + overridemat.Material.TextureLayer[2].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + overridemat.Material.TextureLayer[1].BilinearFilter = + overridemat.Material.TextureLayer[2].BilinearFilter = true; + overridemat.Material.TextureLayer[1].TrilinearFilter = + overridemat.Material.TextureLayer[2].TrilinearFilter = false; + overridemat.Material.TextureLayer[1].AnisotropicFilter = + overridemat.Material.TextureLayer[2].AnisotropicFilter = 0; + overridemat.Material.Wireframe = 1; + overridemat.Enabled = true; + + m_scene_manager->drawAll(scene::ESNRP_SOLID); + + if (m_shadowviz) + { + overridemat.EnableFlags |= video::EMF_WIREFRAME; + m_scene_manager->drawAll(scene::ESNRP_SOLID); + } + + overridemat.EnablePasses = 0; + overridemat.Enabled = false; + camera->activate(); + + tick++; + tick %= 2; + } + + // Lights + if (!m_lightviz) + { + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_TMP1), true, false, + video::SColor(1, 0, 0, 0)); + } else + { + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + } + + const vector3df camcenter = cambox.getCenter(); + const float camradius = cambox.getExtent().getLength() / 2; + const vector3df campos = camnode->getPosition(); + const float camnear = camnode->getNearValue(); + + m_scene_manager->drawAll(scene::ESNRP_CAMERA); + PointLightProvider * const pcb = (PointLightProvider *) irr_driver-> + getCallback(ES_POINTLIGHT); + pcb->updateIPVMatrix(); + SunLightProvider * const scb = (SunLightProvider *) irr_driver-> + getCallback(ES_SUNLIGHT); + scb->updateIPVMatrix(); + FogProvider * const fogcb = (FogProvider *) irr_driver-> + getCallback(ES_FOG); + fogcb->updateIPVMatrix(); + + const u32 lightcount = m_lights.size(); + for (i = 0; i < lightcount; i++) + { + // Sphere culling + const float distance_sq = (m_lights[i]->getPosition() - camcenter).getLengthSQ(); + float radius_sum = camradius + m_lights[i]->getRadius(); + radius_sum *= radius_sum; + if (radius_sum < distance_sq) + continue; + + bool inside = false; + + const float camdistance_sq = (m_lights[i]->getPosition() - campos).getLengthSQ(); + float adjusted_radius = m_lights[i]->getRadius() + camnear; + adjusted_radius *= adjusted_radius; + + // Camera inside the light's radius? Needs adjustment for the near plane. + if (camdistance_sq < adjusted_radius) + { + inside = true; + + video::SMaterial &m = m_lights[i]->getMaterial(0); + m.FrontfaceCulling = true; + m.BackfaceCulling = false; + m.ZBuffer = video::ECFN_GREATER; + } + + if (m_lightviz) + { + overridemat.Enabled = true; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE | video::EMF_WIREFRAME | + video::EMF_FRONT_FACE_CULLING | + video::EMF_BACK_FACE_CULLING | + video::EMF_ZBUFFER; + overridemat.Material.MaterialType = m_shaders->getShader(ES_COLORIZE); + overridemat.Material.Wireframe = true; + overridemat.Material.BackfaceCulling = false; + overridemat.Material.FrontfaceCulling = false; + overridemat.Material.ZBuffer = video::ECFN_LESSEQUAL; + + + ColorizeProvider * const cb = (ColorizeProvider *) m_shaders->m_callbacks[ES_COLORIZE]; + float col[3]; + m_lights[i]->getColor(col); + cb->setColor(col[0], col[1], col[2]); + } + + // Action + m_lights[i]->render(); + + // Reset the inside change + if (inside) + { + video::SMaterial &m = m_lights[i]->getMaterial(0); + m.FrontfaceCulling = false; + m.BackfaceCulling = true; + m.ZBuffer = video::ECFN_LESSEQUAL; + } + + if (m_lightviz) + { + overridemat.Enabled = false; + } + + } // for i in lights + + // Blend lights to the image + video::SMaterial lightmat; + lightmat.Lighting = false; + lightmat.ZWriteEnable = false; + lightmat.ZBuffer = video::ECFN_ALWAYS; + lightmat.setFlag(video::EMF_BILINEAR_FILTER, false); + lightmat.setTexture(0, m_rtts->getRTT(RTT_TMP1)); + // Specular mapping + lightmat.setTexture(1, m_rtts->getRTT(RTT_COLOR)); + lightmat.MaterialType = m_shaders->getShader(ES_LIGHTBLEND); + lightmat.MaterialTypeParam = video::pack_textureBlendFunc(video::EBF_DST_COLOR, video::EBF_ZERO); + lightmat.BlendOperation = video::EBO_ADD; + + lightmat.TextureLayer[0].TextureWrapU = + lightmat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + if (!m_mipviz) + m_post_processing->drawQuad(cam, lightmat); + + if (!bgnodes) + { + // If there are no BG nodes, it's more efficient to do the skybox here. + m_renderpass = scene::ESNRP_SKY_BOX; + m_scene_manager->drawAll(m_renderpass); + } + + // Is the lens flare enabled & visible? Check last frame's query. + const bool hasflare = World::getWorld()->getTrack()->hasLensFlare(); + const bool hasgodrays = World::getWorld()->getTrack()->hasGodRays(); + if (hasflare | hasgodrays) + { + irr::video::COpenGLDriver* gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver(); + + GLuint res; + gl_driver->extGlGetQueryObjectuiv(m_lensflare_query, GL_QUERY_RESULT, &res); + m_post_processing->setSunPixels(res); + + // Prepare the query for the next frame. + gl_driver->extGlBeginQuery(GL_SAMPLES_PASSED_ARB, m_lensflare_query); + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + m_scene_manager->drawAll(scene::ESNRP_CAMERA); + m_sun_interposer->render(); + gl_driver->extGlEndQuery(GL_SAMPLES_PASSED_ARB); + + m_lensflare->setStrength(res / 4000.0f); + + if (hasflare) + m_lensflare->OnRegisterSceneNode(); + + // Make sure the color mask is reset + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + + // Render the post-processed scene for solids + m_post_processing->renderSolid(cam); + + // We need to re-render camera due to the per-cam-node hack. + m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_TRANSPARENT | + scene::ESNRP_TRANSPARENT_EFFECT; + m_scene_manager->drawAll(m_renderpass); + + // Handle displacing nodes, if any + const u32 displacingcount = m_displacing.size(); + if (displacingcount) + { + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_DISPLACE), false, false); + glClearColor(0, 0, 0, 0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, ~0); + glEnable(GL_STENCIL_TEST); + + overridemat.Enabled = 1; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE | video::EMF_TEXTURE0; + overridemat.Material.MaterialType = m_shaders->getShader(ES_DISPLACE); + + overridemat.Material.TextureLayer[0].Texture = + irr_driver->getTexture((file_manager->getTextureDir() + "displace.png").c_str()); + overridemat.Material.TextureLayer[0].BilinearFilter = + overridemat.Material.TextureLayer[0].TrilinearFilter = true; + overridemat.Material.TextureLayer[0].AnisotropicFilter = 0; + overridemat.Material.TextureLayer[0].TextureWrapU = + overridemat.Material.TextureLayer[0].TextureWrapV = video::ETC_REPEAT; + + for (i = 0; i < displacingcount; i++) + { + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + m_displacing[i]->render(); + + m_scene_manager->setCurrentRendertime(scene::ESNRP_TRANSPARENT); + m_displacing[i]->render(); + } + + overridemat.Enabled = 0; + + // Blur it + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, ~0); + + video::SMaterial minimat; + minimat.Lighting = false; + minimat.ZWriteEnable = false; + minimat.ZBuffer = video::ECFN_ALWAYS; + minimat.setFlag(video::EMF_TRILINEAR_FILTER, true); + + minimat.TextureLayer[0].TextureWrapU = + minimat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + + ((GaussianBlurProvider *) m_shaders->m_callbacks[ES_GAUSSIAN3H])->setResolution( + UserConfigParams::m_width, + UserConfigParams::m_height); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN3H); + minimat.setTexture(0, m_rtts->getRTT(RTT_DISPLACE)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_TMP2), true, false); + m_post_processing->drawQuad(cam, minimat); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN3V); + minimat.setTexture(0, m_rtts->getRTT(RTT_TMP2)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_DISPLACE), true, false); + m_post_processing->drawQuad(cam, minimat); + + glDisable(GL_STENCIL_TEST); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + } + + // Drawing for this cam done, cleanup + const u32 glowrepcount = transparent_glow_nodes.size(); + for (i = 0; i < glowrepcount; i++) + { + transparent_glow_nodes[i]->remove(); + transparent_glow_nodes[i]->drop(); + } + + PROFILER_POP_CPU_MARKER(); + + // Note that drawAll must be called before rendering + // the bullet debug view, since otherwise the camera + // is not set up properly. This is only used for + // the bullet debug view. + if (UserConfigParams::m_artist_debug_mode) + World::getWorld()->getPhysics()->draw(); + } // for igetNumKarts() + + // Render the post-processed scene + m_post_processing->render(); + + // Set the viewport back to the full screen for race gui + m_video_driver->setViewPort(core::recti(0, 0, + UserConfigParams::m_width, + UserConfigParams::m_height)); + + for(unsigned int i=0; irenderPlayerView(camera, dt); + + PROFILER_POP_CPU_MARKER(); + } // for iendScene(); + + getPostProcessing()->update(dt); +} + +// -------------------------------------------- + +void IrrDriver::renderFixed(float dt) +{ + World *world = World::getWorld(); // Never NULL. + + m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, + world->getClearColor()); + + irr_driver->getVideoDriver()->enableMaterial2D(); + + RaceGUIBase *rg = world->getRaceGUI(); + if (rg) rg->update(dt); + + + for(unsigned int i=0; iactivate(); + rg->preRenderCallback(camera); // adjusts start referee + + m_renderpass = ~0; + m_scene_manager->drawAll(); + + PROFILER_POP_CPU_MARKER(); + + // Note that drawAll must be called before rendering + // the bullet debug view, since otherwise the camera + // is not set up properly. This is only used for + // the bullet debug view. + if (UserConfigParams::m_artist_debug_mode) + World::getWorld()->getPhysics()->draw(); + } // for igetNumKarts() + + // Set the viewport back to the full screen for race gui + m_video_driver->setViewPort(core::recti(0, 0, + UserConfigParams::m_width, + UserConfigParams::m_height)); + + for(unsigned int i=0; irenderPlayerView(camera, dt); + PROFILER_POP_CPU_MARKER(); + + } // for iendScene(); +} diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp new file mode 100644 index 000000000..77492ced3 --- /dev/null +++ b/src/graphics/rtts.cpp @@ -0,0 +1,154 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/rtts.hpp" + +#include "config/user_config.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "utils/log.hpp" + +RTT::RTT() +{ + using namespace video; + using namespace core; + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + const dimension2du res(UserConfigParams::m_width, UserConfigParams::m_height); + const dimension2du half = res/2; + const dimension2du quarter = res/4; + const dimension2du eighth = res/8; + const dimension2du sixteenth = res/16; + + const dimension2du ssaosize = UserConfigParams::m_ssao == 2 ? res : quarter; + + const u16 shadowside = UserConfigParams::m_shadows == 2 ? 2048 : 512; + const dimension2du shadowsize(shadowside, shadowside); + const dimension2du warpvsize(1, 512); + const dimension2du warphsize(512, 1); + + // The last parameter stands for "has stencil". The name is used in the texture + // cache, and when saving textures to files as the default name. + // + // All RTTs are currently RGBA8 with stencil. The four tmp RTTs are the same size + // as the screen, for use in post-processing. + // + // Optionally, the collapse ones use a smaller format. + + bool stencil = true; + rtts[RTT_TMP1] = drv->addRenderTargetTexture(res, "rtt.tmp1", ECF_A8R8G8B8, stencil); + if(!rtts[RTT_TMP1]) + { + // Work around for intel hd3000 cards :( + stencil = false; + rtts[RTT_TMP1] = drv->addRenderTargetTexture(res, "rtt.tmp1", ECF_A8R8G8B8, stencil); + Log::error("rtts", "Stencils for rtt not available, most likely a driver bug."); + if(UserConfigParams::m_pixel_shaders) + { + Log::error("rtts", "This requires pixel shaders to be disabled."); + UserConfigParams::m_pixel_shaders = false; + } + } + rtts[RTT_TMP2] = drv->addRenderTargetTexture(res, "rtt.tmp2", ECF_A8R8G8B8, stencil); + rtts[RTT_TMP3] = drv->addRenderTargetTexture(res, "rtt.tmp3", ECF_A8R8G8B8, stencil); + rtts[RTT_TMP4] = drv->addRenderTargetTexture(res, "rtt.tmp4", ECF_A8R8G8B8, stencil); + rtts[RTT_DEPTH] = drv->addRenderTargetTexture(res, "rtt.depth", ECF_A8R8G8B8, stencil); + rtts[RTT_NORMAL] = drv->addRenderTargetTexture(res, "rtt.normal", ECF_A8R8G8B8, stencil); + rtts[RTT_COLOR] = drv->addRenderTargetTexture(res, "rtt.color", ECF_A8R8G8B8, stencil); + + rtts[RTT_HALF1] = drv->addRenderTargetTexture(half, "rtt.half1", ECF_A8R8G8B8, stencil); + rtts[RTT_HALF2] = drv->addRenderTargetTexture(half, "rtt.half2", ECF_A8R8G8B8, stencil); + + rtts[RTT_QUARTER1] = drv->addRenderTargetTexture(quarter, "rtt.q1", ECF_A8R8G8B8, stencil); + rtts[RTT_QUARTER2] = drv->addRenderTargetTexture(quarter, "rtt.q2", ECF_A8R8G8B8, stencil); + rtts[RTT_QUARTER3] = drv->addRenderTargetTexture(quarter, "rtt.q3", ECF_A8R8G8B8, stencil); + + rtts[RTT_EIGHTH1] = drv->addRenderTargetTexture(eighth, "rtt.e1", ECF_A8R8G8B8, stencil); + rtts[RTT_EIGHTH2] = drv->addRenderTargetTexture(eighth, "rtt.e2", ECF_A8R8G8B8, stencil); + + rtts[RTT_SIXTEENTH1] = drv->addRenderTargetTexture(sixteenth, "rtt.s1", ECF_A8R8G8B8, stencil); + rtts[RTT_SIXTEENTH2] = drv->addRenderTargetTexture(sixteenth, "rtt.s2", ECF_A8R8G8B8, stencil); + + rtts[RTT_SSAO1] = drv->addRenderTargetTexture(ssaosize, "rtt.ssao1", ECF_A8R8G8B8, stencil); + rtts[RTT_SSAO2] = drv->addRenderTargetTexture(ssaosize, "rtt.ssao2", ECF_A8R8G8B8, stencil); + + rtts[RTT_SHADOW] = drv->addRenderTargetTexture(shadowsize, "rtt.shadow", ECF_A8R8G8B8, stencil); + rtts[RTT_WARPV] = drv->addRenderTargetTexture(warpvsize, "rtt.warpv", ECF_A8R8G8B8, stencil); + rtts[RTT_WARPH] = drv->addRenderTargetTexture(warphsize, "rtt.warph", ECF_A8R8G8B8, stencil); + + rtts[RTT_DISPLACE] = drv->addRenderTargetTexture(res, "rtt.displace", ECF_A8R8G8B8, stencil); + + if (((COpenGLDriver *) drv)->queryOpenGLFeature(COpenGLDriver::IRR_ARB_texture_rg)) + { + // Use optimized formats if supported + rtts[RTT_COLLAPSE] = drv->addRenderTargetTexture(shadowsize, "rtt.collapse", ECF_R8, stencil); + + rtts[RTT_COLLAPSEV] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev", ECF_R8, stencil); + rtts[RTT_COLLAPSEH] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh", ECF_R8, stencil); + rtts[RTT_COLLAPSEV2] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev2", ECF_R8, stencil); + rtts[RTT_COLLAPSEH2] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh2", ECF_R8, stencil); + + rtts[RTT_HALF_SOFT] = drv->addRenderTargetTexture(half, "rtt.halfsoft", ECF_R8, stencil); + } else + { + rtts[RTT_COLLAPSE] = drv->addRenderTargetTexture(shadowsize, "rtt.collapse", ECF_A8R8G8B8, stencil); + + rtts[RTT_COLLAPSEV] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev", ECF_A8R8G8B8, stencil); + rtts[RTT_COLLAPSEH] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh", ECF_A8R8G8B8, stencil); + rtts[RTT_COLLAPSEV2] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev2", ECF_A8R8G8B8, stencil); + rtts[RTT_COLLAPSEH2] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh2", ECF_A8R8G8B8, stencil); + + rtts[RTT_HALF_SOFT] = drv->addRenderTargetTexture(half, "rtt.halfsoft", ECF_A8R8G8B8, stencil); + } + + u32 i; + for (i = 0; i < RTT_COUNT; i++) + { + if (!rtts[i]) + Log::fatal("RTT", "Failed to create a RTT"); + } + + // Clear those that should be cleared + drv->beginScene(false, false); + + drv->setRenderTarget(rtts[RTT_SSAO1], true, false, SColor(255, 255, 255, 255)); + drv->setRenderTarget(rtts[RTT_SSAO2], true, false, SColor(255, 255, 255, 255)); + + drv->setRenderTarget(rtts[RTT_COLLAPSEV], true, false); + drv->setRenderTarget(rtts[RTT_COLLAPSEH], true, false); + drv->setRenderTarget(rtts[RTT_COLLAPSEV2], true, false); + drv->setRenderTarget(rtts[RTT_COLLAPSEH2], true, false); + + drv->setRenderTarget(0, false, false); + + drv->endScene(); +} + +RTT::~RTT() +{ + u32 i; + for (i = 0; i < RTT_COUNT; i++) + { + irr_driver->removeTexture(rtts[i]); + } +} + +ITexture *RTT::getRTT(TypeRTT which) +{ + assert(which < RTT_COUNT); + return rtts[which]; +} diff --git a/src/network/race_result_message.hpp b/src/graphics/rtts.hpp similarity index 51% rename from src/network/race_result_message.hpp rename to src/graphics/rtts.hpp index 95d3a6152..8000c02b9 100644 --- a/src/network/race_result_message.hpp +++ b/src/graphics/rtts.hpp @@ -1,6 +1,4 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,30 +14,70 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_RESULT_MESSAGE_HPP -#define HEADER_RACE_RESULT_MESSAGE_HPP +#ifndef HEADER_RTTS_HPP +#define HEADER_RTTS_HPP -#include +namespace irr { + namespace video { + class ITexture; + }; +}; -#include "network/message.hpp" +using irr::video::ITexture; - -/** This message is from the server to all clients to inform them about the - * result of a race. The clients wait for this message before they finish - * a race. - */ -class RaceResultMessage : public Message +enum TypeRTT +{ + RTT_TMP1 = 0, + RTT_TMP2, + RTT_TMP3, + RTT_TMP4, + RTT_DEPTH, + RTT_NORMAL, + RTT_COLOR, + + RTT_HALF1, + RTT_HALF2, + + RTT_QUARTER1, + RTT_QUARTER2, + RTT_QUARTER3, + + RTT_EIGHTH1, + RTT_EIGHTH2, + + RTT_SIXTEENTH1, + RTT_SIXTEENTH2, + + RTT_SSAO1, + RTT_SSAO2, + + RTT_SHADOW, + RTT_COLLAPSE, + RTT_COLLAPSEH, + RTT_COLLAPSEV, + RTT_COLLAPSEH2, + RTT_COLLAPSEV2, + RTT_WARPH, + RTT_WARPV, + + RTT_HALF_SOFT, + + RTT_DISPLACE, + + RTT_COUNT +}; + +class RTT { - struct RaceResult { - float m_time; - int m_score; - }; // RaceResult -private: - std::vector m_all_results; public: - RaceResultMessage(); - RaceResultMessage(ENetPacket* pkt); - void addRaceResult(int kart_id, float time, int points); - void getRaceResult(int kart_id, float &time, int &points); -}; // RaceResultMessage + RTT(); + ~RTT(); + + ITexture *getRTT(TypeRTT which); + +private: + ITexture *rtts[RTT_COUNT]; +}; + #endif + diff --git a/src/graphics/screenquad.cpp b/src/graphics/screenquad.cpp new file mode 100644 index 000000000..da2a8a176 --- /dev/null +++ b/src/graphics/screenquad.cpp @@ -0,0 +1,30 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 "graphics/screenquad.hpp" + +// Just the static parts to our screenquad + +const u16 ScreenQuad::indices[4] = {0, 1, 2, 3}; + +static const SColor white(255, 255, 255, 255); + +const S3DVertex ScreenQuad::vertices[4] = { + S3DVertex(-1, 1, 0, 0, 1, 0, white, 0, 1), + S3DVertex(1, 1, 0, 0, 1, 0, white, 1, 1), + S3DVertex(-1, -1, 0, 0, 1, 0, white, 0, 0), + S3DVertex(1, -1, 0, 0, 1, 0, white, 1, 0), + }; diff --git a/src/graphics/screenquad.hpp b/src/graphics/screenquad.hpp new file mode 100644 index 000000000..478500946 --- /dev/null +++ b/src/graphics/screenquad.hpp @@ -0,0 +1,91 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 HEADER_SCREENQUAD_H +#define HEADER_SCREENQUAD_H + +#include "graphics/glwrap.hpp" + +#include +#include + +using namespace irr; +using namespace video; + +class ScreenQuad { + +public: + ScreenQuad(IVideoDriver *xy) + { + vd = xy; + + mat.Lighting = false; + mat.ZBuffer = video::ECFN_ALWAYS; + mat.ZWriteEnable = false; + + for(u32 c = 0; c < MATERIAL_MAX_TEXTURES; c++) + { + mat.TextureLayer[c].TextureWrapU = video::ETC_CLAMP_TO_EDGE; + mat.TextureLayer[c].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + } + } + + SMaterial& getMaterial() { return mat; } + + //Set the texture to render with the quad + void setTexture(ITexture* tex, u32 layer = 0) + { + mat.TextureLayer[layer].Texture = tex; + } + + ITexture* getTexture(u32 layer = 0) const { return mat.TextureLayer[layer].Texture; } + + void setMaterialType(E_MATERIAL_TYPE mt) { mat.MaterialType = mt; } + + void render(bool setRTToFrameBuff = true) const + { + if(setRTToFrameBuff) + vd->setRenderTarget(video::ERT_FRAME_BUFFER); + + sq_dorender(); + } + + void render(ITexture* rt) const + { + vd->setRenderTarget(rt); + + sq_dorender(); + } + +protected: + static const S3DVertex vertices[4]; + static const u16 indices[4]; + SMaterial mat; + + IVideoDriver* vd; + + void sq_dorender() const + { + vd->setMaterial(mat); + vd->setTransform(ETS_WORLD, core::IdentityMatrix); + vd->setTransform(ETS_VIEW, core::IdentityMatrix); + vd->setTransform(ETS_PROJECTION, core::IdentityMatrix); + vd->drawVertexPrimitiveList(vertices, 4, indices, 2, EVT_STANDARD, + scene::EPT_TRIANGLE_STRIP); + } +}; + +#endif diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp new file mode 100644 index 000000000..c62800249 --- /dev/null +++ b/src/graphics/shaders.cpp @@ -0,0 +1,264 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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. + +#define SHADER_NAMES + +#include "graphics/callbacks.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/shaders.hpp" +#include "io/file_manager.hpp" +#include "utils/log.hpp" + +#include +#include + +using namespace video; + +Shaders::Shaders() +{ + const std::string &dir = file_manager->getShaderDir(); + + IGPUProgrammingServices * const gpu = irr_driver->getVideoDriver()->getGPUProgrammingServices(); + + #define glsl(a, b, c) gpu->addHighLevelShaderMaterialFromFiles((a).c_str(), (b).c_str(), (IShaderConstantSetCallBack*) c) + #define glslmat(a, b, c, d) gpu->addHighLevelShaderMaterialFromFiles((a).c_str(), (b).c_str(), (IShaderConstantSetCallBack*) c, d) + #define glsl_noinput(a, b) gpu->addHighLevelShaderMaterialFromFiles((a).c_str(), (b).c_str(), (IShaderConstantSetCallBack*) 0) + + // Callbacks + memset(m_callbacks, 0, sizeof(m_callbacks)); + + m_callbacks[ES_NORMAL_MAP_LIGHTMAP] = new NormalMapProvider(true); + m_callbacks[ES_NORMAL_MAP] = new NormalMapProvider(false); + m_callbacks[ES_SPLATTING] = new SplattingProvider(); + m_callbacks[ES_WATER] = new WaterShaderProvider(); + m_callbacks[ES_GRASS] = new GrassShaderProvider(); + m_callbacks[ES_COLOR_LEVELS] = new ColorLevelsProvider(); + m_callbacks[ES_BUBBLES] = new BubbleEffectProvider(); + m_callbacks[ES_RAIN] = new RainEffectProvider(); + m_callbacks[ES_SNOW] = new SnowEffectProvider(); + m_callbacks[ES_MOTIONBLUR] = new MotionBlurProvider(); + m_callbacks[ES_GAUSSIAN3V] = m_callbacks[ES_GAUSSIAN3H] = new GaussianBlurProvider(); + m_callbacks[ES_MIPVIZ] = new MipVizProvider(); + m_callbacks[ES_COLORIZE] = new ColorizeProvider(); + m_callbacks[ES_GLOW] = new GlowProvider(); + m_callbacks[ES_OBJECTPASS] = new ObjectPassProvider(); + m_callbacks[ES_LIGHTBLEND] = new LightBlendProvider(); + m_callbacks[ES_POINTLIGHT] = new PointLightProvider(); + m_callbacks[ES_SUNLIGHT] = new SunLightProvider(); + m_callbacks[ES_BLOOM] = new BloomProvider(); + m_callbacks[ES_MLAA_COLOR1] = new MLAAColor1Provider(); + m_callbacks[ES_MLAA_BLEND2] = new MLAABlend2Provider(); + m_callbacks[ES_MLAA_NEIGH3] = new MLAANeigh3Provider(); + m_callbacks[ES_SSAO] = new SSAOProvider(); + m_callbacks[ES_GODRAY] = new GodRayProvider(); + m_callbacks[ES_SHADOWPASS] = new ShadowPassProvider(); + m_callbacks[ES_SHADOW_IMPORTANCE] = new ShadowImportanceProvider(); + m_callbacks[ES_COLLAPSE] = new CollapseProvider(); + m_callbacks[ES_BLOOM_POWER] = new BloomPowerProvider(); + m_callbacks[ES_MULTIPLY_ADD] = new MultiplyProvider(); + m_callbacks[ES_SHADOWGEN] = new ShadowGenProvider(); + m_callbacks[ES_CAUSTICS] = new CausticsProvider(); + m_callbacks[ES_DISPLACE] = new DisplaceProvider(); + m_callbacks[ES_PPDISPLACE] = new PPDisplaceProvider(); + m_callbacks[ES_FOG] = new FogProvider(); + + // Ok, go + m_shaders[ES_NORMAL_MAP] = glslmat(dir + "normalmap.vert", dir + "normalmap.frag", + m_callbacks[ES_NORMAL_MAP], EMT_SOLID_2_LAYER); + + m_shaders[ES_NORMAL_MAP_LIGHTMAP] = glslmat(dir + "normalmap.vert", dir + "normalmap.frag", + m_callbacks[ES_NORMAL_MAP_LIGHTMAP], EMT_SOLID_2_LAYER); + + m_shaders[ES_SPLATTING] = glsl(dir + "objectpass.vert", dir + "splatting.frag", + m_callbacks[ES_SPLATTING]); + + m_shaders[ES_WATER] = glslmat(dir + "water.vert", dir + "water.frag", + m_callbacks[ES_WATER], EMT_TRANSPARENT_ALPHA_CHANNEL); + m_shaders[ES_WATER_SURFACE] = glsl(dir + "water.vert", dir + "pass.frag", + m_callbacks[ES_WATER]); + + m_shaders[ES_SPHERE_MAP] = glslmat(dir + "objectpass_rimlit.vert", dir + "objectpass_spheremap.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + + m_shaders[ES_GRASS] = glslmat(dir + "grass.vert", dir + "grass.frag", + m_callbacks[ES_GRASS], EMT_TRANSPARENT_ALPHA_CHANNEL); + m_shaders[ES_GRASS_REF] = glslmat(dir + "grass.vert", dir + "grass.frag", + m_callbacks[ES_GRASS], EMT_TRANSPARENT_ALPHA_CHANNEL_REF); + + m_shaders[ES_BUBBLES] = glslmat(dir + "bubble.vert", dir + "bubble.frag", + m_callbacks[ES_BUBBLES], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_RAIN] = glslmat(dir + "rain.vert", dir + "rain.frag", + m_callbacks[ES_RAIN], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_SNOW] = glslmat(dir + "snow.vert", dir + "snow.frag", + m_callbacks[ES_SNOW], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_MOTIONBLUR] = glsl(std::string(""), dir + "motion_blur.frag", + m_callbacks[ES_MOTIONBLUR]); + + m_shaders[ES_GAUSSIAN3H] = glslmat(std::string(""), dir + "gaussian3h.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_GAUSSIAN3V] = glslmat(std::string(""), dir + "gaussian3v.frag", + m_callbacks[ES_GAUSSIAN3V], EMT_SOLID); + + m_shaders[ES_GAUSSIAN6H] = glslmat(std::string(""), dir + "gaussian6h.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_GAUSSIAN6V] = glslmat(std::string(""), dir + "gaussian6v.frag", + m_callbacks[ES_GAUSSIAN3V], EMT_SOLID); + + m_shaders[ES_MIPVIZ] = glslmat(std::string(""), dir + "mipviz.frag", + m_callbacks[ES_MIPVIZ], EMT_SOLID); + + m_shaders[ES_FLIP] = glslmat(std::string(""), dir + "flip.frag", + 0, EMT_SOLID); + m_shaders[ES_FLIP_ADDITIVE] = glslmat(std::string(""), dir + "flip.frag", + 0, EMT_TRANSPARENT_ADD_COLOR); + + m_shaders[ES_COLOR_LEVELS] = glslmat(std::string(""), dir + "color_levels.frag", + m_callbacks[ES_COLOR_LEVELS], EMT_SOLID); + + m_shaders[ES_BLOOM] = glslmat(std::string(""), dir + "bloom.frag", + m_callbacks[ES_BLOOM], EMT_SOLID); + + m_shaders[ES_COLORIZE] = glslmat(std::string(""), dir + "colorize.frag", + m_callbacks[ES_COLORIZE], EMT_SOLID); + m_shaders[ES_COLORIZE_REF] = glslmat(std::string(""), dir + "colorize_ref.frag", + m_callbacks[ES_COLORIZE], EMT_SOLID); + + m_shaders[ES_PASS] = glslmat(std::string(""), dir + "pass.frag", + 0, EMT_SOLID); + m_shaders[ES_PASS_ADDITIVE] = glslmat(std::string(""), dir + "pass.frag", + 0, EMT_TRANSPARENT_ADD_COLOR); + + m_shaders[ES_GLOW] = glslmat(std::string(""), dir + "glow.frag", + m_callbacks[ES_GLOW], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_OBJECTPASS] = glslmat(dir + "objectpass.vert", dir + "objectpass.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + m_shaders[ES_OBJECTPASS_REF] = glslmat(dir + "objectpass.vert", dir + "objectpass_ref.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + m_shaders[ES_OBJECTPASS_RIMLIT] = glslmat(dir + "objectpass_rimlit.vert", dir + "objectpass_rimlit.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + + m_shaders[ES_LIGHTBLEND] = glslmat(std::string(""), dir + "lightblend.frag", + m_callbacks[ES_LIGHTBLEND], EMT_ONETEXTURE_BLEND); + + m_shaders[ES_POINTLIGHT] = glslmat(std::string(""), dir + "pointlight.frag", + m_callbacks[ES_POINTLIGHT], EMT_ONETEXTURE_BLEND); + + m_shaders[ES_SUNLIGHT] = glslmat(std::string(""), dir + "sunlight.frag", + m_callbacks[ES_SUNLIGHT], EMT_SOLID); + m_shaders[ES_SUNLIGHT_SHADOW] = glslmat(dir + "pass.vert", dir + "sunlightshadow.frag", + m_callbacks[ES_SUNLIGHT], EMT_SOLID); + + m_shaders[ES_MLAA_COLOR1] = glsl(dir + "mlaa_offset.vert", dir + "mlaa_color1.frag", + m_callbacks[ES_MLAA_COLOR1]); + m_shaders[ES_MLAA_BLEND2] = glsl(dir + "pass.vert", dir + "mlaa_blend2.frag", + m_callbacks[ES_MLAA_BLEND2]); + m_shaders[ES_MLAA_NEIGH3] = glsl(dir + "mlaa_offset.vert", dir + "mlaa_neigh3.frag", + m_callbacks[ES_MLAA_NEIGH3]); + + m_shaders[ES_SSAO] = glsl(std::string(""), dir + "ssao.frag", m_callbacks[ES_SSAO]); + + m_shaders[ES_GODFADE] = glsl(std::string(""), dir + "godfade.frag", m_callbacks[ES_COLORIZE]); + m_shaders[ES_GODRAY] = glsl(std::string(""), dir + "godray.frag", m_callbacks[ES_GODRAY]); + + m_shaders[ES_SHADOWPASS] = glsl(dir + "shadowpass.vert", dir + "shadowpass.frag", + m_callbacks[ES_SHADOWPASS]); + + m_shaders[ES_SHADOW_IMPORTANCE] = glsl(dir + "shadowimportance.vert", + dir + "shadowimportance.frag", + m_callbacks[ES_SHADOW_IMPORTANCE]); + + m_shaders[ES_COLLAPSE] = glsl(std::string(""), dir + "collapse.frag", + m_callbacks[ES_COLLAPSE]); + m_shaders[ES_SHADOW_WARPH] = glsl(std::string(""), dir + "shadowwarph.frag", + m_callbacks[ES_COLLAPSE]); + m_shaders[ES_SHADOW_WARPV] = glsl(std::string(""), dir + "shadowwarpv.frag", + m_callbacks[ES_COLLAPSE]); + + m_shaders[ES_BLOOM_POWER] = glsl(std::string(""), dir + "bloompower.frag", + m_callbacks[ES_BLOOM_POWER]); + m_shaders[ES_BLOOM_BLEND] = glslmat(std::string(""), dir + "bloomblend.frag", + 0, EMT_TRANSPARENT_ADD_COLOR); + + m_shaders[ES_MULTIPLY_ADD] = glslmat(std::string(""), dir + "multiply.frag", + m_callbacks[ES_MULTIPLY_ADD], EMT_ONETEXTURE_BLEND); + + m_shaders[ES_PENUMBRAH] = glslmat(std::string(""), dir + "penumbrah.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_PENUMBRAV] = glslmat(std::string(""), dir + "penumbrav.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_SHADOWGEN] = glslmat(std::string(""), dir + "shadowgen.frag", + m_callbacks[ES_SHADOWGEN], EMT_SOLID); + + m_shaders[ES_CAUSTICS] = glslmat(std::string(""), dir + "caustics.frag", + m_callbacks[ES_CAUSTICS], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_DISPLACE] = glsl(dir + "displace.vert", dir + "displace.frag", + m_callbacks[ES_DISPLACE]); + m_shaders[ES_PPDISPLACE] = glsl(std::string(""), dir + "ppdisplace.frag", + m_callbacks[ES_PPDISPLACE]); + + m_shaders[ES_PASSFAR] = glsl(dir + "farplane.vert", dir + "colorize.frag", + m_callbacks[ES_COLORIZE]); + + m_shaders[ES_FOG] = glslmat(std::string(""), dir + "fog.frag", + m_callbacks[ES_FOG], EMT_ONETEXTURE_BLEND); + + // Check that all successfully loaded + u32 i; + for (i = 0; i < ES_COUNT; i++) { + + // Old Intel Windows drivers fail here. + // It's an artist option, so not necessary to play. + if (i == ES_MIPVIZ) + continue; + + check(i); + } + + #undef glsl + #undef glslmat + #undef glsl_noinput +} + +Shaders::~Shaders() +{ + u32 i; + for (i = 0; i < ES_COUNT; i++) + { + if (i == ES_GAUSSIAN3V || !m_callbacks[i]) continue; + delete m_callbacks[i]; + } +} + +E_MATERIAL_TYPE Shaders::getShader(const ShaderType num) const +{ + assert(num < ES_COUNT); + + return (E_MATERIAL_TYPE) m_shaders[num]; +} + +void Shaders::check(const int num) const +{ + if (m_shaders[num] == -1) + { + Log::fatal("shaders", "Shader %s failed to load. Update your drivers, if the issue " + "persists, report a bug to us.", shader_names[num] + 3); + } +} diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp new file mode 100644 index 000000000..2f9127a2b --- /dev/null +++ b/src/graphics/shaders.hpp @@ -0,0 +1,118 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 HEADER_SHADERS_HPP +#define HEADER_SHADERS_HPP + +#include +#include +#include +using namespace irr; + +#define FOREACH_SHADER(ACT) \ + ACT(ES_NORMAL_MAP) \ + ACT(ES_NORMAL_MAP_LIGHTMAP) \ + ACT(ES_SPLATTING) \ + ACT(ES_WATER) \ + ACT(ES_WATER_SURFACE) \ + ACT(ES_SPHERE_MAP) \ + ACT(ES_GRASS) \ + ACT(ES_GRASS_REF) \ + ACT(ES_BUBBLES) \ + ACT(ES_RAIN) \ + ACT(ES_SNOW) \ + ACT(ES_MOTIONBLUR) \ + ACT(ES_GAUSSIAN3H) \ + ACT(ES_GAUSSIAN3V) \ + ACT(ES_MIPVIZ) \ + ACT(ES_FLIP) \ + ACT(ES_FLIP_ADDITIVE) \ + ACT(ES_COLOR_LEVELS) \ + ACT(ES_BLOOM) \ + ACT(ES_GAUSSIAN6H) \ + ACT(ES_GAUSSIAN6V) \ + ACT(ES_COLORIZE) \ + ACT(ES_COLORIZE_REF) \ + ACT(ES_PASS) \ + ACT(ES_PASS_ADDITIVE) \ + ACT(ES_GLOW) \ + ACT(ES_OBJECTPASS) \ + ACT(ES_OBJECTPASS_REF) \ + ACT(ES_LIGHTBLEND) \ + ACT(ES_POINTLIGHT) \ + ACT(ES_SUNLIGHT) \ + ACT(ES_SUNLIGHT_SHADOW) \ + ACT(ES_OBJECTPASS_RIMLIT) \ + ACT(ES_MLAA_COLOR1) \ + ACT(ES_MLAA_BLEND2) \ + ACT(ES_MLAA_NEIGH3) \ + ACT(ES_SSAO) \ + ACT(ES_GODFADE) \ + ACT(ES_GODRAY) \ + ACT(ES_SHADOWPASS) \ + ACT(ES_SHADOW_IMPORTANCE) \ + ACT(ES_COLLAPSE) \ + ACT(ES_SHADOW_WARPH) \ + ACT(ES_SHADOW_WARPV) \ + ACT(ES_BLOOM_POWER) \ + ACT(ES_BLOOM_BLEND) \ + ACT(ES_MULTIPLY_ADD) \ + ACT(ES_PENUMBRAH) \ + ACT(ES_PENUMBRAV) \ + ACT(ES_SHADOWGEN) \ + ACT(ES_CAUSTICS) \ + ACT(ES_DISPLACE) \ + ACT(ES_PPDISPLACE) \ + ACT(ES_PASSFAR) \ + ACT(ES_FOG) + +#define ENUM(a) a, +#define STR(a) #a, + +enum ShaderType +{ + FOREACH_SHADER(ENUM) + + ES_COUNT +}; + +#ifdef SHADER_NAMES +static const char *shader_names[] = { + FOREACH_SHADER(STR) +}; +#endif + +class Shaders +{ +public: + Shaders(); + ~Shaders(); + + video::E_MATERIAL_TYPE getShader(const ShaderType num) const; + + video::IShaderConstantSetCallBack * m_callbacks[ES_COUNT]; + +private: + void check(const int num) const; + + int m_shaders[ES_COUNT]; +}; + +#undef ENUM +#undef STR +#undef FOREACH_SHADER + +#endif diff --git a/src/graphics/shadow.cpp b/src/graphics/shadow.cpp index 464453eb8..e092f8284 100644 --- a/src/graphics/shadow.cpp +++ b/src/graphics/shadow.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -29,6 +29,7 @@ Shadow::Shadow(video::ITexture *texture, scene::ISceneNode *node, float scale = m.setTexture(0, texture); m.BackfaceCulling = false; m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + m.setFlag(video::EMF_ZWRITE_ENABLE , false); m_mesh = irr_driver->createQuadMesh(&m, /*create_one_quad*/true); scene::IMeshBuffer *buffer = m_mesh->getMeshBuffer(0); irr::video::S3DVertex* v=(video::S3DVertex*)buffer->getVertices(); diff --git a/src/graphics/shadow.hpp b/src/graphics/shadow.hpp index 4a7d23e8c..d55977bde 100644 --- a/src/graphics/shadow.hpp +++ b/src/graphics/shadow.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/shadow_importance.cpp b/src/graphics/shadow_importance.cpp new file mode 100644 index 000000000..9f89a8cac --- /dev/null +++ b/src/graphics/shadow_importance.cpp @@ -0,0 +1,169 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "config/user_config.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/large_mesh_buffer.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/per_camera_node.hpp" +#include "graphics/shadow_importance.hpp" +#include "graphics/shaders.hpp" +#include "graphics/rtts.hpp" +#include "utils/vs.hpp" + +#include + +using namespace video; +using namespace scene; +using namespace core; + +// The actual ShadowImportance node +class ShadowImportanceNode: public scene::ISceneNode +{ +public: + ShadowImportanceNode(scene::ISceneManager* mgr) + : scene::ISceneNode(0, mgr, -1) + { + mat.Lighting = false; + mat.ZWriteEnable = false; + mat.MaterialType = irr_driver->getShader(ES_SHADOW_IMPORTANCE); + + mat.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + mat.setTexture(1, irr_driver->getRTT(RTT_DEPTH)); + mat.setTexture(2, irr_driver->getRTT(RTT_COLOR)); + + mat.setFlag(EMF_BILINEAR_FILTER, false); + + u32 i; + for (i = 0; i < MATERIAL_MAX_TEXTURES; i++) + { + mat.TextureLayer[i].TextureWrapU = + mat.TextureLayer[i].TextureWrapV = ETC_CLAMP_TO_EDGE; + } + + // Low shadows only back-project every other pixel + const u32 incr = UserConfigParams::m_shadows < 2 ? 2 : 1; + + count = (UserConfigParams::m_width * UserConfigParams::m_height) / (incr * incr); + + // Fill in the mesh buffer + buf.Vertices.clear(); + buf.Indices.clear(); + + buf.Vertices.set_used(count); + buf.Indices.set_used(count); + + buf.Primitive = EPT_POINTS; + buf.setHardwareMappingHint(EHM_STATIC); + + const float halfx = 0.5f / UserConfigParams::m_width; + const float halfy = 0.5f / UserConfigParams::m_height; + + list = glGenLists(1); + + s32 x, y; + i = 0; + glNewList(list, GL_COMPILE); + glBegin(GL_POINTS); + for (x = 0; x < UserConfigParams::m_width; x += incr) + { + const float xpos = ((float) x) / UserConfigParams::m_width + halfx; + + for (y = 0; y < UserConfigParams::m_height; y += incr) + { + const float ypos = ((float) y) / UserConfigParams::m_height + halfy; + + buf.Indices[i] = i; + buf.Vertices[i] = S3DVertex(xpos, ypos, 0, 0, 0, 0, + SColor(255, 255, 255, 255), 0, 0); + + glVertex2s((int)roundf(xpos * 32767), (int)roundf(ypos * 32767)); + i++; + } + } + glEnd(); + glEndList(); + + box.addInternalPoint(vector3df(-1)); + box.addInternalPoint(vector3df(1)); + } + + ~ShadowImportanceNode() + { + } + + virtual void render() + { + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setMaterial(mat); + + drv->setTransform(ETS_WORLD, IdentityMatrix); + +// drv->drawMeshBuffer(&buf); + // Setup the env for drawing our list by drawing one point + drv->drawVertexPrimitiveList(buf.getVertices(), 1, buf.getIndices(), 1, + EVT_STANDARD, EPT_POINTS); + glCallList(list); + } + + virtual const core::aabbox3d& getBoundingBox() const + { + return box; + } + + virtual void OnRegisterSceneNode() + { + ISceneNode::OnRegisterSceneNode(); + } + + virtual u32 getMaterialCount() const { return 1; } + virtual video::SMaterial& getMaterial(u32 i) { return mat; } + +private: + video::SMaterial mat; + core::aabbox3d box; + u32 count; + GLuint list; + + scene::LargeMeshBuffer buf; +}; + +// The ShadowImportance manager + +ShadowImportance::ShadowImportance() +{ + m_node = new ShadowImportanceNode(irr_driver->getSceneManager()); + m_node->setAutomaticCulling(0); +} // ShadowImportance + +// ---------------------------------------------------------------------------- + +ShadowImportance::~ShadowImportance() +{ + m_node->drop(); // drop STK's reference + m_node->remove(); // Then remove it from the scene graph. +} + +// ---------------------------------------------------------------------------- + +void ShadowImportance::render() +{ + m_node->render(); +} diff --git a/src/graphics/shadow_importance.hpp b/src/graphics/shadow_importance.hpp new file mode 100644 index 000000000..3d5c2dba0 --- /dev/null +++ b/src/graphics/shadow_importance.hpp @@ -0,0 +1,41 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 HEADER_SHADOW_IMPORTANCE_HPP +#define HEADER_SHADOW_IMPORTANCE_HPP + +#include +namespace irr +{ + namespace video { class SMaterial; class ITexture; } + namespace scene { class ICameraSceneNode; class ISceneNode; } +} +using namespace irr; + +class ShadowImportance +{ + scene::ISceneNode *m_node; + +public: + ShadowImportance(); + ~ShadowImportance(); + + void render(); +}; + +#endif diff --git a/src/graphics/show_curve.cpp b/src/graphics/show_curve.cpp index f372cd613..f4113dcd9 100644 --- a/src/graphics/show_curve.cpp +++ b/src/graphics/show_curve.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/show_curve.hpp b/src/graphics/show_curve.hpp index 6a6086819..d3b63bf70 100644 --- a/src/graphics/show_curve.hpp +++ b/src/graphics/show_curve.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/skid_marks.cpp b/src/graphics/skid_marks.cpp index 99f62fe27..e8f0eb40a 100644 --- a/src/graphics/skid_marks.cpp +++ b/src/graphics/skid_marks.cpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Ingo Ruhnke +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2013-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -102,12 +103,11 @@ void SkidMarks::update(float dt, bool force_skid_marks, // Get raycast information // ----------------------- - const btWheelInfo::RaycastInfo &raycast_right = - m_kart.getVehicle()->getWheelInfo(2).m_raycastInfo; - const btWheelInfo::RaycastInfo raycast_left = - m_kart.getVehicle()->getWheelInfo(3).m_raycastInfo; - Vec3 delta = raycast_right.m_contactPointWS - - raycast_left.m_contactPointWS; + const btKart *vehicle = m_kart.getVehicle(); + const Vec3& raycast_right = vehicle->getVisualContactPoint(2); + const Vec3& raycast_left = vehicle->getVisualContactPoint(3); + + Vec3 delta = raycast_right - raycast_left; // The kart is making skid marks when it's: // - forced to leave skid marks, or all of: @@ -118,7 +118,7 @@ void SkidMarks::update(float dt, bool force_skid_marks, // If only one wheel touches the ground, the 2nd one gets the same // raycast result --> delta is 0, which is considered to be not skidding. const Skidding *skid = m_kart.getSkidding(); - bool is_skidding = raycast_right.m_isInContact && + bool is_skidding = vehicle->visualWheelsTouchGround() && ( force_skid_marks || ( (skid->getSkidState()==Skidding::SKID_ACCUMULATE_LEFT|| skid->getSkidState()==Skidding::SKID_ACCUMULATE_RIGHT ) @@ -141,20 +141,18 @@ void SkidMarks::update(float dt, bool force_skid_marks, // ------------------------------------------------- delta.normalize(); - delta *= m_width; + delta *= m_width*0.5f; float distance = 0.0f; Vec3 start = m_left[m_current]->getCenterStart(); - Vec3 newPoint = (raycast_left.m_contactPointWS + raycast_right.m_contactPointWS)/2; + Vec3 newPoint = (raycast_left + raycast_right)/2; // this linear distance does not account for the kart turning, it's true, // but it produces good enough results distance = (newPoint - start).length(); - m_left [m_current]->add(raycast_left.m_contactPointWS, - raycast_left.m_contactPointWS + delta, + m_left [m_current]->add(raycast_left-delta, raycast_left+delta, distance); - m_right[m_current]->add(raycast_right.m_contactPointWS-delta, - raycast_right.m_contactPointWS, + m_right[m_current]->add(raycast_right-delta, raycast_right+delta, distance); // Adjust the boundary box of the mesh to include the // adjusted aabb of its buffers. @@ -173,22 +171,20 @@ void SkidMarks::update(float dt, bool force_skid_marks, // Start new skid marks // -------------------- // No skidmarking if wheels don't have contact - if(!raycast_right.m_isInContact) return; + if(!vehicle->visualWheelsTouchGround()) return; if(delta.length2()<0.0001) return; delta.normalize(); - delta *= m_width; + delta *= m_width*0.5f; SkidMarkQuads *smq_left = - new SkidMarkQuads(raycast_left.m_contactPointWS, - raycast_left.m_contactPointWS + delta, + new SkidMarkQuads(raycast_left-delta, raycast_left+delta , m_material, m_avoid_z_fighting, custom_color); scene::SMesh *new_mesh = new scene::SMesh(); new_mesh->addMeshBuffer(smq_left); SkidMarkQuads *smq_right = - new SkidMarkQuads(raycast_right.m_contactPointWS - delta, - raycast_right.m_contactPointWS, + new SkidMarkQuads(raycast_right-delta, raycast_right+delta, m_material, m_avoid_z_fighting, custom_color); new_mesh->addMeshBuffer(smq_right); scene::IMeshSceneNode *new_node = irr_driver->addMesh(new_mesh); @@ -198,7 +194,7 @@ void SkidMarks::update(float dt, bool force_skid_marks, #endif // We don't keep a reference to the mesh here, so we have to decrement - // the reference count (which is set to 1 when doing "new SMesh()". + // the reference count (which is set to 1 when doing "new SMesh())". // The scene node will keep the mesh alive. new_mesh->drop(); m_current++; diff --git a/src/graphics/skid_marks.hpp b/src/graphics/skid_marks.hpp index 5bf229087..3f94e720d 100644 --- a/src/graphics/skid_marks.hpp +++ b/src/graphics/skid_marks.hpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Ingo Ruhnke +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2013-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/slip_stream.cpp b/src/graphics/slip_stream.cpp index d06f020eb..e2cccdc4f 100644 --- a/src/graphics/slip_stream.cpp +++ b/src/graphics/slip_stream.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/slip_stream.hpp b/src/graphics/slip_stream.hpp index 57d40483f..4de166027 100644 --- a/src/graphics/slip_stream.hpp +++ b/src/graphics/slip_stream.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/graphics/stars.cpp b/src/graphics/stars.cpp index b0570287c..3361835ef 100644 --- a/src/graphics/stars.cpp +++ b/src/graphics/stars.cpp @@ -1,5 +1,7 @@ // SuperTuxKart - a fun racing game with go-kart // +// Copyright (C) 2012-2013 SuperTuxKart-Team +// // 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 diff --git a/src/graphics/stars.hpp b/src/graphics/stars.hpp index 5f8dbfc03..a100ee518 100644 --- a/src/graphics/stars.hpp +++ b/src/graphics/stars.hpp @@ -1,5 +1,7 @@ // SuperTuxKart - a fun racing game with go-kart // +// Copyright (C) 2012-2013 SuperTuxKart-Team +// // 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 diff --git a/src/graphics/sun.cpp b/src/graphics/sun.cpp new file mode 100644 index 000000000..e9c029822 --- /dev/null +++ b/src/graphics/sun.cpp @@ -0,0 +1,161 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/sun.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/screenquad.hpp" +#include "graphics/shaders.hpp" +#include "io/file_manager.hpp" +#include "modes/world.hpp" +#include "tracks/track.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +SunNode::SunNode(scene::ISceneManager* mgr, float r, float g, float b): + LightNode(mgr, 10000, r, g, b) +{ + + sq = new ScreenQuad(irr_driver->getVideoDriver()); + + SMaterial &m = sq->getMaterial(); + + m.MaterialType = irr_driver->getShader(ES_SUNLIGHT); + m.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + m.setTexture(1, irr_driver->getRTT(RTT_DEPTH)); + m.setTexture(2, irr_driver->getTexture((file_manager->getTextureDir() + "cloudshadow.png").c_str())); + m.setFlag(EMF_BILINEAR_FILTER, false); + + if (UserConfigParams::m_shadows) + { + m.setTexture(3, irr_driver->getRTT(RTT_SHADOW)); + m.setTexture(4, irr_driver->getRTT(RTT_WARPH)); + m.setTexture(5, irr_driver->getRTT(RTT_WARPV)); + + m.TextureLayer[4].BilinearFilter = + m.TextureLayer[5].BilinearFilter = true; + + m.MaterialType = irr_driver->getShader(ES_SUNLIGHT_SHADOW); + } + + for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; i++) + { + m.TextureLayer[i].TextureWrapU = m.TextureLayer[i].TextureWrapV = + ETC_CLAMP_TO_EDGE; + } + + m.TextureLayer[2].TextureWrapU = m.TextureLayer[2].TextureWrapV = ETC_REPEAT; + + m.TextureLayer[2].TrilinearFilter = true; + + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; +} + +SunNode::~SunNode() +{ + delete sq; +} + +void SunNode::render() +{ + SunLightProvider * const cb = (SunLightProvider *) irr_driver->getCallback(ES_SUNLIGHT); + cb->setColor(m_color[0], m_color[1], m_color[2]); + + vector3df pos = getPosition(); + pos.normalize(); + cb->setPosition(pos.X, pos.Y, pos.Z); + + if (!UserConfigParams::m_shadows || !World::getWorld()->getTrack()->hasShadows()) + { + sq->render(false); + return; + } + + array mrt; + mrt.reallocate(2); + mrt.push_back(irr_driver->getRTT(RTT_TMP2)); + mrt.push_back(irr_driver->getRTT(RTT_TMP3)); + irr_driver->getVideoDriver()->setRenderTarget(mrt, true, false); + + // Render the sun lighting to tmp2, shadow map to tmp3 + sq->render(false); + + // Filter the shadow map for soft shadows + // Note that quarter1 is reserved for glow during this time. + // Having a separate RTT for glow would work, but be wasted VRAM due to less reuse. + ScreenQuad tmpsq(irr_driver->getVideoDriver()); + GaussianBlurProvider * const gcb = (GaussianBlurProvider *) irr_driver->getCallback(ES_GAUSSIAN3H); + + gcb->setResolution(UserConfigParams::m_width / 2, UserConfigParams::m_height / 2); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAH)); + tmpsq.setTexture(irr_driver->getRTT(RTT_TMP3)); + tmpsq.render(irr_driver->getRTT(RTT_HALF1)); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAV)); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF1)); + tmpsq.render(irr_driver->getRTT(RTT_HALF2)); + + gcb->setResolution(UserConfigParams::m_width / 4, UserConfigParams::m_height / 4); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAH)); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF2)); + tmpsq.render(irr_driver->getRTT(RTT_QUARTER2)); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAV)); + tmpsq.setTexture(irr_driver->getRTT(RTT_QUARTER2)); + tmpsq.render(irr_driver->getRTT(RTT_QUARTER3)); + + gcb->setResolution(UserConfigParams::m_width / 8, UserConfigParams::m_height / 8); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAH)); + tmpsq.setTexture(irr_driver->getRTT(RTT_QUARTER3)); + tmpsq.render(irr_driver->getRTT(RTT_EIGHTH1)); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAV)); + tmpsq.setTexture(irr_driver->getRTT(RTT_EIGHTH1)); + tmpsq.render(irr_driver->getRTT(RTT_EIGHTH2)); + + // Use these to generate a new soft shadow map + tmpsq.setMaterialType(irr_driver->getShader(ES_SHADOWGEN)); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF2), 0); + tmpsq.setTexture(irr_driver->getRTT(RTT_QUARTER3), 1); + tmpsq.setTexture(irr_driver->getRTT(RTT_EIGHTH2), 2); + + irr_driver->getVideoDriver()->setRenderTarget(irr_driver->getRTT(RTT_HALF_SOFT), true, false); + tmpsq.render(false); + + tmpsq.setTexture(0, 0); + tmpsq.setTexture(0, 1); + tmpsq.setTexture(0, 2); + tmpsq.setTexture(0, 3); + + // Combine them back to the lighting RTT + tmpsq.setMaterialType(irr_driver->getShader(ES_MULTIPLY_ADD)); + tmpsq.setTexture(irr_driver->getRTT(RTT_TMP2), 0); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF_SOFT), 1); + + tmpsq.getMaterial().MaterialTypeParam = pack_textureBlendFunc(EBF_ONE, EBF_ONE); + tmpsq.getMaterial().BlendOperation = EBO_ADD; + + irr_driver->getVideoDriver()->setRenderTarget(irr_driver->getRTT(RTT_TMP1), false, false); + tmpsq.render(false); +} diff --git a/src/network/race_info_message.hpp b/src/graphics/sun.hpp similarity index 68% rename from src/network/race_info_message.hpp rename to src/graphics/sun.hpp index 52404b3f5..387444c30 100644 --- a/src/network/race_info_message.hpp +++ b/src/graphics/sun.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 Lauri Kasanen // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,18 +16,25 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_INFO_MESSAGE_HPP -#define HEADER_RACE_INFO_MESSAGE_HPP +#ifndef HEADER_SUN_HPP +#define HEADER_SUN_HPP -#include +#include "graphics/light.hpp" +#include "utils/cpp2011.h" -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" +class ScreenQuad; -class RaceInfoMessage : public Message +// The actual rain node +class SunNode: public LightNode { public: - RaceInfoMessage(const std::vector& kart_info); - RaceInfoMessage(ENetPacket* pkt); -}; // RaceInfoMessage + SunNode(scene::ISceneManager* mgr, float r, float g, float b); + virtual ~SunNode(); + + virtual void render() OVERRIDE; + +private: + ScreenQuad *sq; +}; + #endif diff --git a/src/graphics/water.cpp b/src/graphics/water.cpp new file mode 100644 index 000000000..3ac11d981 --- /dev/null +++ b/src/graphics/water.cpp @@ -0,0 +1,94 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/water.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +WaterNode::WaterNode(scene::ISceneManager* mgr, IMesh *mesh, float height, float speed, + float length): + IMeshSceneNode(mgr->getRootSceneNode(), mgr, -1) +{ + m_mat = mesh->getMeshBuffer(0)->getMaterial(); + + m_mat.Lighting = false; + + if (m_mat.MaterialType != irr_driver->getShader(ES_WATER)) + { + m_mat.MaterialType = irr_driver->getShader(ES_WATER_SURFACE); + } else + { + m_mat.BlendOperation = EBO_ADD; + } + + m_mat.setFlag(EMF_ZWRITE_ENABLE, false); + + m_mesh = mesh; + mesh->grab(); + mesh->setHardwareMappingHint(EHM_STATIC); + m_box = mesh->getBoundingBox(); + + m_height = height; + m_speed = speed; + m_length = length; +} + +WaterNode::~WaterNode() +{ + m_mesh->drop(); +} + +void WaterNode::render() +{ + if (SceneManager->getSceneNodeRenderPass() != scene::ESNRP_TRANSPARENT) + return; + + WaterShaderProvider * const cb = (WaterShaderProvider *) irr_driver->getCallback(ES_WATER); + cb->setSpeed(m_speed); + cb->setHeight(m_height); + cb->setLength(m_length); + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(m_mat); + + const u32 max = m_mesh->getMeshBufferCount(); + for (u32 i = 0; i < max; i++) + { + drv->drawMeshBuffer(m_mesh->getMeshBuffer(i)); + } +} + +void WaterNode::OnRegisterSceneNode() +{ + if (IsVisible) + { + SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); + ISceneNode::OnRegisterSceneNode(); + } +} diff --git a/src/graphics/water.hpp b/src/graphics/water.hpp new file mode 100644 index 000000000..15acacb6f --- /dev/null +++ b/src/graphics/water.hpp @@ -0,0 +1,72 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 HEADER_WATER_HPP +#define HEADER_WATER_HPP + +#include +#include + +using namespace irr; + +namespace irr +{ + namespace scene { class IMesh; } +} + +// The actual node +class WaterNode: public scene::IMeshSceneNode +{ +public: + WaterNode(scene::ISceneManager* mgr, scene::IMesh *mesh, float height, float speed, + float length); + virtual ~WaterNode(); + + virtual void render() OVERRIDE; + + virtual const core::aabbox3d& getBoundingBox() const OVERRIDE + { + return m_box; + } + + virtual void OnRegisterSceneNode() OVERRIDE; + + virtual u32 getMaterialCount() const OVERRIDE { return 1; } + virtual video::SMaterial& getMaterial(u32 i) OVERRIDE { return m_mat; } + + virtual scene::ESCENE_NODE_TYPE getType() const OVERRIDE { return scene::ESNT_MESH; } + + virtual void setMesh(scene::IMesh *) OVERRIDE {} + virtual scene::IMesh *getMesh() OVERRIDE { return m_mesh; } + virtual void setReadOnlyMaterials(bool) OVERRIDE {} + virtual bool isReadOnlyMaterials() const OVERRIDE { return false; } + virtual scene::IShadowVolumeSceneNode* addShadowVolumeSceneNode + (const scene::IMesh*, int, bool, f32) OVERRIDE { return NULL; } + +protected: + video::SMaterial m_mat; + core::aabbox3df m_box; + + scene::IMesh *m_mesh; + + float m_height; + float m_speed; + float m_length; +}; + +#endif diff --git a/src/graphics/wind.cpp b/src/graphics/wind.cpp new file mode 100644 index 000000000..5bddd4a15 --- /dev/null +++ b/src/graphics/wind.cpp @@ -0,0 +1,40 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 "graphics/irr_driver.hpp" +#include "graphics/wind.hpp" +#include "utils/helpers.hpp" + +Wind::Wind() +{ + m_seed = (float)((rand() % 1000) - 500); +} + +vector3df Wind::getWind() const +{ + return m_wind; +} + +void Wind::update() +{ + vector3df dir(0, 0, 1); + const u32 now = irr_driver->getDevice()->getTimer()->getTime(); + + const float rotation = asinf(noise2d(m_seed, now / 100000.0f)) * RAD_TO_DEGREE; + dir.rotateXZBy(rotation); + + m_wind = dir; +} diff --git a/src/network/kart_update_message.hpp b/src/graphics/wind.hpp similarity index 73% rename from src/network/kart_update_message.hpp rename to src/graphics/wind.hpp index 7d3a38fc4..651258f2b 100644 --- a/src/network/kart_update_message.hpp +++ b/src/graphics/wind.hpp @@ -1,6 +1,4 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,15 +14,25 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_KART_UPDATE_MESSAGE_HPP -#define HEADER_KART_UPDATE_MESSAGE_HPP +#ifndef HEADER_WIND_HPP +#define HEADER_WIND_HPP -#include "network/message.hpp" +#include +using namespace irr; +using core::vector3df; -class KartUpdateMessage : public Message +class Wind { public: - KartUpdateMessage(); - KartUpdateMessage(ENetPacket* pkt); -}; // KartUpdateMessage + Wind(); + + vector3df getWind() const; + void update(); + +private: + vector3df m_wind; + float m_seed; +}; + #endif + diff --git a/src/guiengine/CGUISpriteBank.cpp b/src/guiengine/CGUISpriteBank.cpp index 0bcadeb89..8c756453a 100644 --- a/src/guiengine/CGUISpriteBank.cpp +++ b/src/guiengine/CGUISpriteBank.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2002-2009 Nikolaus Gebhardt, modified by Marianne Gagnon +// Copyright (C) 2002-2013 Nikolaus Gebhardt, modified by Marianne Gagnon // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h diff --git a/src/guiengine/abstract_state_manager.cpp b/src/guiengine/abstract_state_manager.cpp index af93d5397..e27a54d14 100644 --- a/src/guiengine/abstract_state_manager.cpp +++ b/src/guiengine/abstract_state_manager.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/abstract_state_manager.hpp b/src/guiengine/abstract_state_manager.hpp index 98a054862..e280103ac 100644 --- a/src/guiengine/abstract_state_manager.hpp +++ b/src/guiengine/abstract_state_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/abstract_top_level_container.cpp b/src/guiengine/abstract_top_level_container.cpp index c8ed60363..72073ed94 100644 --- a/src/guiengine/abstract_top_level_container.cpp +++ b/src/guiengine/abstract_top_level_container.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/abstract_top_level_container.hpp b/src/guiengine/abstract_top_level_container.hpp index e75d3cb71..1f7bb874f 100644 --- a/src/guiengine/abstract_top_level_container.hpp +++ b/src/guiengine/abstract_top_level_container.hpp @@ -1,5 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -76,12 +77,12 @@ namespace GUIEngine Widget* getWidget(const int id); /** This function searches and returns a widget by name, cast as specified type, - * if that widget is found and the type is correct. - * \param name The name of the widget to find - * \return an object by name, casted to specified type, or NULL if + * if that widget is found and the type is correct. + * \param name The name of the widget to find + * \return an object by name, casted to specified type, or NULL if * not found/wrong type */ template - T* getWidget(const char* name) + T* getWidget(const char* name) { Widget* out = getWidget(name); T* outCasted = dynamic_cast( out ); diff --git a/src/guiengine/dialog_queue.cpp b/src/guiengine/dialog_queue.cpp new file mode 100644 index 000000000..0a390f224 --- /dev/null +++ b/src/guiengine/dialog_queue.cpp @@ -0,0 +1,82 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "guiengine/dialog_queue.hpp" +#include "assert.h" + +using namespace GUIEngine; + +namespace GUIEngine +{ + static DialogQueue* dialog_queue_singleton(NULL); + + DialogQueue* DialogQueue::get() + { + if (dialog_queue_singleton == NULL) + dialog_queue_singleton = new DialogQueue(); + return dialog_queue_singleton; + } + + void DialogQueue::deallocate() + { + delete dialog_queue_singleton; + dialog_queue_singleton = NULL; + } // deallocate + + + // ---------------------------------------------------------------------------- + + DialogQueue::DialogQueue() + { + m_closer = NULL; + } + + // ---------------------------------------------------------------------------- + + void DialogQueue::pushDialog(ModalDialog * dialog, bool closes_any_dialog) + { + assert(!dialog->isInited()); + if(closes_any_dialog) + { + delete m_closer; + m_closer = dialog; + } + else + m_queue.push(dialog); + } + + // ---------------------------------------------------------------------------- + + void DialogQueue::update() + { + if(m_closer != NULL) + { + ModalDialog::dismiss(); + m_closer->load(); + m_closer = NULL; + } + else if(!m_queue.empty()) + { + ModalDialog * entry = m_queue.front(); + if(!ModalDialog::isADialogActive()) + { + entry->load(); + m_queue.pop(); + } + } + } +} diff --git a/src/network/network_kart.hpp b/src/guiengine/dialog_queue.hpp similarity index 54% rename from src/network/network_kart.hpp rename to src/guiengine/dialog_queue.hpp index 892439a24..1a8a51271 100644 --- a/src/network/network_kart.hpp +++ b/src/guiengine/dialog_queue.hpp @@ -1,6 +1,5 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 Glenn De Jonghe // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,22 +15,33 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NETWORK_KART_HPP -#define HEADER_NETWORK_KART_HPP +#ifndef HEADER_DIALOG_QUEUE_HPP +#define HEADER_DIALOG_QUEUE_HPP -#include "karts/kart.hpp" +#include +#include "guiengine/modaldialog.hpp" -class Track; - -class NetworkKart : public Kart +/** + * \ingroup guiengine + */ +namespace GUIEngine { -private: - int m_global_player_id; // to identify this kart to the network manager -public: - NetworkKart(const std::string& kart_name, unsigned int world_kart_id, - int position, const btTransform& init_transform, - int global_player_id, RaceManager::KartType type); - void setControl(const KartControl& kc); - virtual bool isNetworkKart() const { return true; } -}; // NetworkKart + + class DialogQueue + { + private : + + std::queue m_queue; + ModalDialog * m_closer; + DialogQueue(); + public : + /**Singleton */ + static DialogQueue * get(); + static void deallocate(); + void pushDialog(ModalDialog * dialog, bool closes_any_dialog = false); + void update(); + + }; + +} #endif diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index 4b9cdadf4..56691ad6f 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -485,7 +485,7 @@ namespace GUIEngine Used on divs, indicate by how many pixels to pad contents - + \n
\section code Using the engine in code @@ -657,6 +657,7 @@ namespace GUIEngine #include "guiengine/screen.hpp" #include "guiengine/skin.hpp" #include "guiengine/widget.hpp" +#include "guiengine/dialog_queue.hpp" #include "modes/demo_world.hpp" #include "modes/world.hpp" #include "states_screens/race_gui_base.hpp" @@ -835,6 +836,7 @@ namespace GUIEngine { widget->update(dt); } + DialogQueue::get()->update(); } // Hack : on the first frame, irrlicht processes all events that have been queued @@ -915,6 +917,25 @@ namespace GUIEngine g_loaded_screens.push_back(cutscene); } // addScreenToList + // ------------------------------------------------------------------------ + + void removeScreen(const char* name) + { + const int screen_amount = g_loaded_screens.size(); + for(int n=0; nunload(); + delete g_current_screen; + g_current_screen = NULL; + g_loaded_screens.remove(n); + break; + } + } + } + // ------------------------------------------------------------------------ void reshowCurrentScreen() { @@ -953,6 +974,8 @@ namespace GUIEngine //delete g_small_font; g_small_font->drop(); g_small_font = NULL; + g_large_font->drop(); + g_large_font = NULL; g_digit_font->drop(); g_digit_font = NULL; @@ -1259,6 +1282,11 @@ namespace GUIEngine ITexture* loading = irr_driver->getTexture(file_manager->getGUIDir()+"loading.png"); + if(!loading) + { + Log::fatal("Engine", "Can not find loading.png texture, aborting."); + exit(-1); + } const int texture_w = loading->getSize().Width; const int texture_h = loading->getSize().Height; diff --git a/src/guiengine/engine.hpp b/src/guiengine/engine.hpp index 9a91cdd84..e3fad5da7 100644 --- a/src/guiengine/engine.hpp +++ b/src/guiengine/engine.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -195,6 +195,8 @@ namespace GUIEngine /** \brief Add a screen to the list of screens known by the gui engine */ void addScreenToList(Screen* screen); + /** \brief Remove a screen from the list of screens known by the gui engine */ + void removeScreen(const char* name); /** \brief Low-level mean to change current screen. * \note Do not use directly. Use a state manager instead to get higher-level functionnality. diff --git a/src/guiengine/event_handler.cpp b/src/guiengine/event_handler.cpp index 8aadb8563..6720113cf 100644 --- a/src/guiengine/event_handler.cpp +++ b/src/guiengine/event_handler.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,6 +22,7 @@ #include #include +#include "graphics/irr_driver.hpp" #include "guiengine/abstract_state_manager.hpp" #include "guiengine/engine.hpp" #include "guiengine/modaldialog.hpp" @@ -192,14 +193,18 @@ bool EventHandler::OnEvent (const SEvent &event) #else return true; // EVENT_BLOCK #endif - + const std::string &error_info = irr_driver->getTextureErrorMessage(); if (event.LogEvent.Level == irr::ELL_WARNING) { - printf("[Irrlicht Warning] %s\n", event.LogEvent.Text); + if(error_info.size()>0) + Log::warn("EventHandler", error_info.c_str()); + Log::warn("Irrlicht", event.LogEvent.Text); } else if (event.LogEvent.Level == irr::ELL_ERROR) { - printf("[Irrlicht Error] %s\n", event.LogEvent.Text); + if(error_info.size()>0) + Log::error("EventHandler", error_info.c_str()); + Log::error("Irrlicht", event.LogEvent.Text); } } return true; @@ -684,13 +689,14 @@ EventPropagation EventHandler::onGUIEvent(const SEvent& event) { Widget* w = GUIEngine::getWidget(id); if (w == NULL) break; - if (w->m_deactivated) { GUIEngine::getCurrentScreen()->onDisabledItemClicked(w->m_properties[PROP_ID].c_str()); return EVENT_BLOCK; } + w->onClick(); + // These events are only triggered by mouse (or so I hope) // The player that owns the mouser receives "game master" priviledges return onWidgetActivated(w, PLAYER_ID_GAME_MASTER); @@ -736,7 +742,7 @@ EventPropagation EventHandler::onGUIEvent(const SEvent& event) if (playerID == -1) break; if (input_manager->masterPlayerOnly() && playerID != PLAYER_ID_GAME_MASTER) break; - if (ribbon->mouseHovered(w, playerID) == EVENT_LET) sendEventToUser(ribbon, ribbon->m_properties[PROP_ID], playerID); + ribbon->mouseHovered(w, playerID); if (ribbon->m_event_handler != NULL) ribbon->m_event_handler->mouseHovered(w, playerID); ribbon->setFocusForPlayer(playerID); } diff --git a/src/guiengine/event_handler.hpp b/src/guiengine/event_handler.hpp index a73995b39..f4207f4e8 100644 --- a/src/guiengine/event_handler.hpp +++ b/src/guiengine/event_handler.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/layout_manager.cpp b/src/guiengine/layout_manager.cpp index deb937aea..714a2b7de 100644 --- a/src/guiengine/layout_manager.cpp +++ b/src/guiengine/layout_manager.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -33,13 +33,10 @@ using namespace video; #include "io/file_manager.hpp" #include "utils/ptr_vector.hpp" #include "utils/string_utils.hpp" +#include "utils/vs.hpp" using namespace GUIEngine; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - /** Like atoi, but on error prints an error message to stderr */ int atoi_p(const char* val) { @@ -326,10 +323,10 @@ void LayoutManager::applyCoords(Widget* self, AbstractTopLevelContainer* topLeve else if (self->m_relative_y > -1) self->m_y = (int)(parent_y + parent_h*self->m_relative_y/100); if (self->m_absolute_w > -1) self->m_w = self->m_absolute_w; - else if (self->m_relative_w > -1) self->m_w = (int)round(parent_w*self->m_relative_w/100.0); + else if (self->m_relative_w > -1) self->m_w = (int)roundf(parent_w*self->m_relative_w/100.0f); if (self->m_absolute_h > -1) self->m_h = self->m_absolute_h; - else if (self->m_relative_h > -1) self->m_h = (int)round(parent_h*self->m_relative_h/100.0); + else if (self->m_relative_h > -1) self->m_h = (int)roundf(parent_h*self->m_relative_h/100.0f); // ---- can't make widget bigger than parent if (self->m_h > (int)parent_h) diff --git a/src/guiengine/layout_manager.hpp b/src/guiengine/layout_manager.hpp index 4a009bb3e..4d338e864 100644 --- a/src/guiengine/layout_manager.hpp +++ b/src/guiengine/layout_manager.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/modaldialog.cpp b/src/guiengine/modaldialog.cpp index 60f25b2fe..21d8e5734 100644 --- a/src/guiengine/modaldialog.cpp +++ b/src/guiengine/modaldialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -47,17 +47,19 @@ using namespace GUIEngine; // ---------------------------------------------------------------------------- -ModalDialog::ModalDialog(const float percentWidth, const float percentHeight, - ModalDialogLocation location) +ModalDialog::ModalDialog(const float percentWidth, const float percentHeight, ModalDialogLocation location) { m_dialog_location = location; - doInit(percentWidth, percentHeight); + m_init = false; + m_percent_width = percentWidth; + m_percent_height = percentHeight; } // ---------------------------------------------------------------------------- void ModalDialog::loadFromFile(const char* xmlFile) { + doInit(); IXMLReader* xml = file_manager->createXMLReader( (file_manager->getGUIDir() + xmlFile).c_str() ); if (xml == NULL) { @@ -82,15 +84,17 @@ void ModalDialog::loadFromFile(const char* xmlFile) // ---------------------------------------------------------------------------- -void ModalDialog::doInit(const float percentWidth, const float percentHeight) +void ModalDialog::doInit() { + if(m_init) return; + m_init = true; pointer_was_shown = irr_driver->isPointerShown(); irr_driver->showPointer(); const core::dimension2d& frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize(); - const int w = (int)(frame_size.Width*percentWidth); - const int h = (int)(frame_size.Height*percentHeight); + const int w = (int)(frame_size.Width* m_percent_width); + const int h = (int)(frame_size.Height* m_percent_height); assert(frame_size.Width > 0); assert(frame_size.Height > 0); @@ -187,6 +191,8 @@ void ModalDialog::dismiss() { if(modalWindow != NULL) delete modalWindow; modalWindow = NULL; + if(GUIEngine::getCurrentScreen() != NULL) + GUIEngine::getCurrentScreen()->onDialogClose(); } // ---------------------------------------------------------------------------- diff --git a/src/guiengine/modaldialog.hpp b/src/guiengine/modaldialog.hpp index 731749a94..e5e81e4b1 100644 --- a/src/guiengine/modaldialog.hpp +++ b/src/guiengine/modaldialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -55,11 +55,12 @@ namespace GUIEngine class ModalDialog : public SkinWidgetContainer, public AbstractTopLevelContainer { private: - /** Because C++ doesn't support constructor delegation... */ - void doInit(const float percentWidth, const float percentHeight); ModalDialogLocation m_dialog_location; + float m_percent_width, m_percent_height; + bool m_init; + protected: irr::gui::IGUIWindow* m_irrlicht_window; @@ -85,10 +86,15 @@ namespace GUIEngine * that takes a XML file as argument is used) */ virtual void loadedFromFile() {} + void doInit(); public: LEAK_CHECK() + /** Because C++ doesn't support constructor delegation... */ + + bool isInited() {return m_init;} + virtual ~ModalDialog(); /** Returns whether to block event propagation (usually, you will want to block events you processed) */ @@ -105,7 +111,7 @@ namespace GUIEngine static bool isADialogActive(); /** Override to change what happens on escape pressed */ - virtual void escapePressed() { dismiss(); } + virtual bool onEscapePressed() { return true; } /** Override to be notified of updates */ virtual void onUpdate(float dt) { } @@ -115,6 +121,7 @@ namespace GUIEngine * init(), which is invoked afer widgets were added) */ virtual void beforeAddingWidgets() {} + virtual void load() {} /** \brief Optional callback invoked after widgets have been add()ed */ virtual void init() {} diff --git a/src/guiengine/scalable_font.cpp b/src/guiengine/scalable_font.cpp index 5fcddfea5..a58dd7f31 100644 --- a/src/guiengine/scalable_font.cpp +++ b/src/guiengine/scalable_font.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2002-2009 Nikolaus Gebhardt +// Copyright (C) 2002-2013 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h @@ -32,7 +32,6 @@ ScalableFont::ScalableFont(IGUIEnvironment *env, const io::path& filename) m_fallback_kerning_width = 0; m_fallback_font_scale = 1.0f; m_scale = 1.0f; - m_tab_stop = 0.5f; m_is_hollow_copy = false; m_black_border = false; m_shadow = false; @@ -500,15 +499,12 @@ void ScalableFont::draw(const core::stringw& text, core::position2d offset = position.UpperLeftCorner; core::dimension2d text_dimension; - // When we use the "tab" hack, disable right-alignment, it messes up everything - bool has_tab = (text.findFirst(L'\t') != -1); - - if ((m_rtl && !has_tab) || hcenter || vcenter || clip) + if (m_rtl || hcenter || vcenter || clip) { text_dimension = getDimension(text.c_str()); - if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) / 2; - else if (m_rtl && !has_tab) offset.X += (position.getWidth() - text_dimension.Width); + if (hcenter) offset.X += (position.getWidth() - text_dimension.Width) / 2; + else if (m_rtl) offset.X += (position.getWidth() - text_dimension.Width); if (vcenter) offset.Y += (position.getHeight() - text_dimension.Height) / 2; if (clip) @@ -519,14 +515,6 @@ void ScalableFont::draw(const core::stringw& text, } } - if (m_rtl && has_tab) - { - const int where = text.findFirst(L'\t'); - core::stringw substr = text.subString(0, where-1); - text_dimension = getDimension(substr.c_str()) + getDimension(L"XX"); - offset.X += (int)(position.getWidth()*m_tab_stop-text_dimension.Width); - } - // ---- collect character locations const unsigned int text_size = text.size(); core::array indices(text_size); @@ -537,14 +525,6 @@ void ScalableFont::draw(const core::stringw& text, { wchar_t c = text[i]; - //hack: one tab character is supported, it moves the cursor to the tab stop - if (c == L'\t') - { - offset.X = (int)(position.UpperLeftCorner.X + - position.getWidth()*m_tab_stop); - continue; - } - if (c == L'\r' || // Windows breaks c == L'\n' ) // Unix breaks { diff --git a/src/guiengine/scalable_font.hpp b/src/guiengine/scalable_font.hpp index c6aed9d81..431dad384 100644 --- a/src/guiengine/scalable_font.hpp +++ b/src/guiengine/scalable_font.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2002-2009 Nikolaus Gebhardt +// Copyright (C) 2002-2013 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h @@ -61,9 +61,6 @@ class ScalableFont : public IGUIFontBitmap bool m_is_hollow_copy; bool m_rtl; - /** Position in range [0..1] of the single tab stop we support */ - float m_tab_stop; - public: LEAK_CHECK() @@ -145,9 +142,6 @@ public: void updateRTL(); - /** \param pos position of the tab stop, in range [0..1] */ - void setTabStop(float pos) { m_tab_stop = pos; } - private: struct SFontArea diff --git a/src/guiengine/screen.cpp b/src/guiengine/screen.cpp index 21b5d4c72..687c0f477 100644 --- a/src/guiengine/screen.cpp +++ b/src/guiengine/screen.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/screen.hpp b/src/guiengine/screen.hpp index b6271c0c1..f9552ae74 100644 --- a/src/guiengine/screen.hpp +++ b/src/guiengine/screen.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -61,6 +61,7 @@ namespace GUIEngine template class ScreenSingleton { + protected: static SCREEN* singleton; public: @@ -302,6 +303,10 @@ namespace GUIEngine int axisDir, int value) {} + /** Callback that gets called when a dialog is closed. + * Can be used to set focus for instance. + */ + virtual void onDialogClose() {} }; } diff --git a/src/guiengine/screen_loader.cpp b/src/guiengine/screen_loader.cpp index 06fb47ce5..62d426595 100644 --- a/src/guiengine/screen_loader.cpp +++ b/src/guiengine/screen_loader.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index ad19093f8..d487a77f5 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -128,7 +128,7 @@ namespace SkinConfig if(node->get("type", &type) == 0) { - Log::error("skin", "All elements must have a type\n"); + Log::error("skin", "All elements must have a type\n"); return; } node->get("state", &state); @@ -155,7 +155,7 @@ namespace SkinConfig if(!root) { Log::error("skin", "Could not read XML file '%s'.", - file.c_str()); + file.c_str()); throw std::runtime_error("Invalid skin file"); } @@ -175,7 +175,7 @@ namespace SkinConfig else { Log::error("skin", "Unknown node in XML file '%s'.", - node->getName().c_str()); + node->getName().c_str()); } }// nend for @@ -814,14 +814,13 @@ void Skin::drawProgress(Widget* w, const core::recti &rect, void Skin::drawRatingBar(Widget *w, const core::recti &rect, const bool pressed, const bool focused) { - static const int step_number = 3; // Harcoded number of step. - + RatingBarWidget *ratingBar = (RatingBarWidget*)w; + const ITexture *texture = SkinConfig::m_render_params["rating::neutral"].getImage(); const int texture_w = texture->getSize().Width / 4; const int texture_h = texture->getSize().Height; const float aspect_ratio = 1.0f; - RatingBarWidget *ratingBar = (RatingBarWidget*)w; const int star_number = ratingBar->getStarNumber(); int star_h = rect.getHeight(); @@ -835,8 +834,18 @@ void Skin::drawRatingBar(Widget *w, const core::recti &rect, } // center horizontally and vertically - const int x_from = rect.UpperLeftCorner.X + (rect.getWidth() - star_w) / 2; - const int y_from = rect.UpperLeftCorner.Y + (rect.getHeight() - star_h) / 2; + const int x_from = rect.UpperLeftCorner.X; + const int y_from = rect.UpperLeftCorner.Y; + + core::recti stars_rect(x_from, y_from, x_from + (star_number * star_w), y_from + star_h); + + if(!w->m_deactivated) + ratingBar->setStepValuesByMouse(irr_driver->getDevice()->getCursorControl()->getPosition(), stars_rect); + + SColor colors[] = { SColor(100,255,255,255), + SColor(100,255,255,255), + SColor(100,255,255,255), + SColor(100,255,255,255) }; for (int i = 0; i < star_number; i++) { @@ -847,14 +856,15 @@ void Skin::drawRatingBar(Widget *w, const core::recti &rect, star_rect.LowerRightCorner.X = x_from + (i + 1) * star_w; star_rect.LowerRightCorner.Y = y_from + star_h; - int step = ratingBar->getStepOfStar(i, step_number); + int step = ratingBar->getStepsOfStar(i); const core::recti source_area(texture_w * step, 0, texture_w * (step + 1), texture_h); GUIEngine::getDriver()->draw2DImage(texture, star_rect, source_area, - 0 /* no clipping */, 0, + 0 /* no clipping */, + (w->m_deactivated || ID_DEBUG) ? colors : 0, true /* alpha */); } @@ -1354,7 +1364,7 @@ void Skin::drawSpinnerChild(const core::recti &rect, Widget* widget, void Skin::drawIconButton(const core::recti &rect, Widget* widget, const bool pressed, bool focused) { - RibbonWidget* parentRibbon = (RibbonWidget*)widget->m_event_handler; + RibbonWidget* parentRibbon = dynamic_cast(widget->m_event_handler); IGUIElement* focusedElem = NULL; if (GUIEngine::getFocusForPlayer(PLAYER_ID_GAME_MASTER) != NULL) { @@ -1627,6 +1637,14 @@ void Skin::renderSections(PtrVector* within_vector) ITexture* tex = irr_driver->getTexture( file_manager->getGUIDir() + "bar.png" ); + if(!tex) + { + std::string file = file_manager->getGUIDir() + "main_help.png"; + tex = irr_driver->getTexture(file); + if(!tex) + Log::fatal("Skin", + "Can't find fallback texture 'main_help.png, aborting."); + } core::recti r1(0, (int)(widget.m_y - 40*y_size), framesize.Width, framesize.Height); core::recti r2(core::dimension2di(0,0), tex->getSize()); @@ -1892,6 +1910,9 @@ void Skin::process3DPane(IGUIElement *element, const core::recti &rect, void doDrawBadge(ITexture* texture, const core::recti& rect, float max_icon_size, bool badge_at_left) { + // In case of a problem + if(!texture) return; + const core::dimension2d& texture_size = texture->getSize(); const float aspectRatio = (float)texture_size.Width / (float)texture_size.Height; @@ -1922,6 +1943,8 @@ void Skin::drawBadgeOn(const Widget* widget, const core::recti& rect) if (widget->m_badges & LOCKED_BADGE) { video::ITexture* texture = irr_driver->getTexture( + file_manager->getTextureFile("gui_lock.png"), + "Can't find '%s'.", file_manager->getTextureFile("gui_lock.png")); float max_icon_size = 0.5f; // Lock badge can be quite big doDrawBadge(texture, rect, max_icon_size, true); @@ -1929,6 +1952,8 @@ void Skin::drawBadgeOn(const Widget* widget, const core::recti& rect) if (widget->m_badges & OK_BADGE) { video::ITexture* texture = irr_driver->getTexture( + file_manager->getTextureFile("green_check.png"), + "Can't find '%s'.", file_manager->getTextureFile("green_check.png")); float max_icon_size = 0.35f; doDrawBadge(texture, rect, max_icon_size, true); @@ -1936,7 +1961,8 @@ void Skin::drawBadgeOn(const Widget* widget, const core::recti& rect) if (widget->m_badges & BAD_BADGE) { video::ITexture* texture = irr_driver->getTexture( - file_manager->getTextureFile("red_mark.png")); + file_manager->getTextureFile("red_mark.png"), + "Can't find red_mark.png"); float max_icon_size = 0.35f; doDrawBadge(texture, rect, max_icon_size, false); } @@ -1944,13 +1970,16 @@ void Skin::drawBadgeOn(const Widget* widget, const core::recti& rect) { float max_icon_size = 0.43f; video::ITexture* texture = irr_driver->getTexture( - file_manager->getTextureFile("cup_bronze.png")); + file_manager->getTextureFile("cup_bronze.png"), + "Can't find cup_bronze.png."); doDrawBadge(texture, rect, max_icon_size, false); } if (widget->m_badges & KEYBOARD_BADGE) { float max_icon_size = 0.43f; video::ITexture* texture = irr_driver->getTexture( + file_manager->getGUIDir() + "keyboard.png", + "Can't find '%s'.", file_manager->getGUIDir() + "keyboard.png"); doDrawBadge(texture, rect, max_icon_size, true); } @@ -1958,6 +1987,8 @@ void Skin::drawBadgeOn(const Widget* widget, const core::recti& rect) { float max_icon_size = 0.43f; video::ITexture* texture = irr_driver->getTexture( + file_manager->getGUIDir() + "gamepad.png", + "Can't find '%s'.", file_manager->getGUIDir() + "gamepad.png"); doDrawBadge(texture, rect, max_icon_size, true); } @@ -1965,6 +1996,8 @@ void Skin::drawBadgeOn(const Widget* widget, const core::recti& rect) { float max_icon_size = 0.43f; video::ITexture* texture = irr_driver->getTexture( + file_manager->getGUIDir() + "hourglass.png", + "Can't find '%s'.", file_manager->getGUIDir() + "hourglass.png"); doDrawBadge(texture, rect, max_icon_size, true); } diff --git a/src/guiengine/skin.hpp b/src/guiengine/skin.hpp index 51657d660..9d4bebc04 100644 --- a/src/guiengine/skin.hpp +++ b/src/guiengine/skin.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widget.cpp b/src/guiengine/widget.cpp index 33d740028..2b67c1f39 100644 --- a/src/guiengine/widget.cpp +++ b/src/guiengine/widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,11 +34,7 @@ using namespace gui; #include "io/file_manager.hpp" #include "utils/string_utils.hpp" #include "utils/translation.hpp" - - -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif +#include "utils/vs.hpp" namespace GUIEngine { @@ -98,6 +94,7 @@ Widget::Widget(WidgetType type, bool reserve_id) m_reserved_id = -1; m_deactivated = false; + m_is_visible = true; m_badges = 0; // set a default value, derivates can override this as they wish @@ -319,7 +316,18 @@ void Widget::setParent(IGUIElement* parent) bool Widget::isVisible() const { - return m_element && m_element->isVisible(); + if (m_element != NULL) + assert(m_element->isVisible() == m_is_visible); + return m_is_visible; +} + +// ----------------------------------------------------------------------------- + +bool Widget::isActivated() const +{ + if (isVisible()) + return !m_deactivated; + return false; } // ----------------------------------------------------------------------------- @@ -330,6 +338,7 @@ void Widget::setVisible(bool visible) { m_element->setVisible(visible); } + m_is_visible = visible; m_deactivated = !visible; const int childrenCount = m_children.size(); @@ -349,4 +358,3 @@ void Widget::moveIrrlichtElement() irr::core::dimension2di(m_w, m_h) ) ); } } - diff --git a/src/guiengine/widget.hpp b/src/guiengine/widget.hpp index db09104f8..874ca87cd 100644 --- a/src/guiengine/widget.hpp +++ b/src/guiengine/widget.hpp @@ -1,5 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -234,9 +235,12 @@ namespace GUIEngine /** A bitmask of which badges to show, if any; choices are *_BADGE, defined above */ int m_badges; - /** A simple flag that can be raised to hide this widget */ + /** A simple flag that can be raised to deactivate this widget */ bool m_deactivated; + /** A flag to indicate whether this widget should be visible or not. */ + bool m_is_visible; + /** Set to false if widget is something that should not receive focus */ bool m_focusable; @@ -323,6 +327,8 @@ namespace GUIEngine /** Returns if the element is visible. */ bool isVisible() const; + bool isActivated() const; + /** * Call to resize/move the widget. Not all widgets can resize gracefully. */ @@ -645,6 +651,9 @@ namespace GUIEngine */ bool ok() const { return (m_magic_number == 0xCAFEC001); } + + /** Gets called when the widget is active and got clicked. (Only works for button widgets for now.) */ + virtual void onClick() { } }; diff --git a/src/guiengine/widgets.hpp b/src/guiengine/widgets.hpp index a2da6c9cc..749cf2f0c 100644 --- a/src/guiengine/widgets.hpp +++ b/src/guiengine/widgets.hpp @@ -1,3 +1,20 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2009-2013 Marianne Gagnon +// +// 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. // A shortcut header to get all widgets diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp index 264f8cf48..5f852c194 100644 --- a/src/guiengine/widgets/CGUIEditBox.cpp +++ b/src/guiengine/widgets/CGUIEditBox.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2002-2011 Nikolaus Gebhardt +// Copyright (C) 2002-2013 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h @@ -17,12 +17,12 @@ #include "utils/time.hpp" /* - todo: - optional scrollbars - ctrl+left/right to select word - double click/ctrl click: word select + drag to select whole words, triple click to select line - optional? dragging selected text - numerical + todo: + optional scrollbars + ctrl+left/right to select word + double click/ctrl click: word select + drag to select whole words, triple click to select line + optional? dragging selected text + numerical */ @@ -33,336 +33,336 @@ StkTime::TimeType getTime() //! constructor CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border, - IGUIEnvironment* environment, IGUIElement* parent, s32 id, - const core::rect& rectangle, bool is_rtl) - : IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false), - Border(border), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0), - OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0), - Operator(0), BlinkStartTime(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0), - WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false), - PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER), - CurrentTextRect(0,0,1,1), FrameRect(rectangle) + IGUIEnvironment* environment, IGUIElement* parent, s32 id, + const core::rect& rectangle, bool is_rtl) + : IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false), + Border(border), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0), + OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0), + Operator(0), BlinkStartTime(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0), + WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false), + PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER), + CurrentTextRect(0,0,1,1), FrameRect(rectangle) { m_rtl = is_rtl; - #ifdef _DEBUG - setDebugName("CGUIEditBox"); - #endif + #ifdef _DEBUG + setDebugName("CGUIEditBox"); + #endif - Text = text; + Text = text; - if (Environment) - Operator = Environment->getOSOperator(); + if (Environment) + Operator = Environment->getOSOperator(); - if (Operator) - Operator->grab(); + if (Operator) + Operator->grab(); - // this element can be tabbed to - setTabStop(true); - setTabOrder(-1); + // this element can be tabbed to + setTabStop(true); + setTabOrder(-1); - IGUISkin *skin = 0; - if (Environment) - skin = Environment->getSkin(); - if (Border && skin) - { - FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - } + IGUISkin *skin = 0; + if (Environment) + skin = Environment->getSkin(); + if (Border && skin) + { + FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; + FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; + FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; + FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; + } - breakText(); + breakText(); - calculateScrollPos(); + calculateScrollPos(); } //! destructor CGUIEditBox::~CGUIEditBox() { - if (OverrideFont) - OverrideFont->drop(); + if (OverrideFont) + OverrideFont->drop(); - if (Operator) - Operator->drop(); + if (Operator) + Operator->drop(); } //! Sets another skin independent font. void CGUIEditBox::setOverrideFont(IGUIFont* font) { - if (OverrideFont == font) - return; + if (OverrideFont == font) + return; - if (OverrideFont) - OverrideFont->drop(); + if (OverrideFont) + OverrideFont->drop(); - OverrideFont = font; + OverrideFont = font; - if (OverrideFont) - OverrideFont->grab(); + if (OverrideFont) + OverrideFont->grab(); - breakText(); + breakText(); } //! Sets another color for the text. void CGUIEditBox::setOverrideColor(video::SColor color) { - OverrideColor = color; - OverrideColorEnabled = true; + OverrideColor = color; + OverrideColorEnabled = true; } video::SColor CGUIEditBox::getOverrideColor() const { - return OverrideColor; + return OverrideColor; } //! Turns the border on or off void CGUIEditBox::setDrawBorder(bool border) { - Border = border; + Border = border; } //! Sets if the text should use the overide color or the color in the gui skin. void CGUIEditBox::enableOverrideColor(bool enable) { - OverrideColorEnabled = enable; + OverrideColorEnabled = enable; } bool CGUIEditBox::isOverrideColorEnabled() const { - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return OverrideColorEnabled; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return OverrideColorEnabled; } //! Enables or disables word wrap void CGUIEditBox::setWordWrap(bool enable) { - WordWrap = enable; - breakText(); + WordWrap = enable; + breakText(); } void CGUIEditBox::updateAbsolutePosition() { core::rect oldAbsoluteRect(AbsoluteRect); - IGUIElement::updateAbsolutePosition(); - if ( oldAbsoluteRect != AbsoluteRect ) - { + IGUIElement::updateAbsolutePosition(); + if ( oldAbsoluteRect != AbsoluteRect ) + { breakText(); - } + } } //! Checks if word wrap is enabled bool CGUIEditBox::isWordWrapEnabled() const { - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return WordWrap; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return WordWrap; } //! Enables or disables newlines. void CGUIEditBox::setMultiLine(bool enable) { - MultiLine = enable; + MultiLine = enable; } //! Checks if multi line editing is enabled bool CGUIEditBox::isMultiLineEnabled() const { - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return MultiLine; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return MultiLine; } void CGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar) { - PasswordBox = passwordBox; - if (PasswordBox) - { - PasswordChar = passwordChar; - setMultiLine(false); - setWordWrap(false); - BrokenText.clear(); - } + PasswordBox = passwordBox; + if (PasswordBox) + { + PasswordChar = passwordChar; + setMultiLine(false); + setWordWrap(false); + BrokenText.clear(); + } } bool CGUIEditBox::isPasswordBox() const { - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return PasswordBox; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return PasswordBox; } //! Sets text justification void CGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical) { - HAlign = horizontal; - VAlign = vertical; + HAlign = horizontal; + VAlign = vertical; } //! called if an event happened. bool CGUIEditBox::OnEvent(const SEvent& event) { - if (isEnabled()) - { + if (isEnabled()) + { - switch(event.EventType) - { - case EET_GUI_EVENT: - if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) - { - if (event.GUIEvent.Caller == this) - { - MouseMarking = false; - setTextMarkers(0,0); - } - } - break; - case EET_KEY_INPUT_EVENT: - if (processKey(event)) - return true; - break; - case EET_MOUSE_INPUT_EVENT: - if (processMouse(event)) - return true; - break; - default: - break; - } - } + switch(event.EventType) + { + case EET_GUI_EVENT: + if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) + { + if (event.GUIEvent.Caller == this) + { + MouseMarking = false; + setTextMarkers(0,0); + } + } + break; + case EET_KEY_INPUT_EVENT: + if (processKey(event)) + return true; + break; + case EET_MOUSE_INPUT_EVENT: + if (processMouse(event)) + return true; + break; + default: + break; + } + } - return IGUIElement::OnEvent(event); + return IGUIElement::OnEvent(event); } bool CGUIEditBox::processKey(const SEvent& event) { - if (!event.KeyInput.PressedDown) - return false; + if (!event.KeyInput.PressedDown) + return false; - bool textChanged = false; - s32 newMarkBegin = MarkBegin; - s32 newMarkEnd = MarkEnd; + bool textChanged = false; + s32 newMarkBegin = MarkBegin; + s32 newMarkEnd = MarkEnd; - // control shortcut handling + // control shortcut handling - if (event.KeyInput.Control) - { - // german backlash '\' entered with control + '?' - if ( event.KeyInput.Char == '\\' ) - { - inputChar(event.KeyInput.Char); - return true; - } + if (event.KeyInput.Control) + { + // german backlash '\' entered with control + '?' + if ( event.KeyInput.Char == '\\' ) + { + inputChar(event.KeyInput.Char); + return true; + } - switch(event.KeyInput.Key) - { - case KEY_KEY_A: - // select all - newMarkBegin = 0; - newMarkEnd = Text.size(); - break; - case KEY_KEY_C: - // copy to clipboard - if (!PasswordBox && Operator && MarkBegin != MarkEnd) - { - const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; - const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; + switch(event.KeyInput.Key) + { + case KEY_KEY_A: + // select all + newMarkBegin = 0; + newMarkEnd = Text.size(); + break; + case KEY_KEY_C: + // copy to clipboard + if (!PasswordBox && Operator && MarkBegin != MarkEnd) + { + const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; + const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; - core::stringc s; - s = Text.subString(realmbgn, realmend - realmbgn).c_str(); - Operator->copyToClipboard(s.c_str()); - } - break; - case KEY_KEY_X: - // cut to the clipboard - if (!PasswordBox && Operator && MarkBegin != MarkEnd) - { - const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; - const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; + core::stringc s; + s = Text.subString(realmbgn, realmend - realmbgn).c_str(); + Operator->copyToClipboard(s.c_str()); + } + break; + case KEY_KEY_X: + // cut to the clipboard + if (!PasswordBox && Operator && MarkBegin != MarkEnd) + { + const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; + const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; - // copy - core::stringc sc; - sc = Text.subString(realmbgn, realmend - realmbgn).c_str(); - Operator->copyToClipboard(sc.c_str()); + // copy + core::stringc sc; + sc = Text.subString(realmbgn, realmend - realmbgn).c_str(); + Operator->copyToClipboard(sc.c_str()); - if (isEnabled()) - { - // delete - core::stringw s; - s = Text.subString(0, realmbgn); - s.append( Text.subString(realmend, Text.size()-realmend) ); - Text = s; + if (isEnabled()) + { + // delete + core::stringw s; + s = Text.subString(0, realmbgn); + s.append( Text.subString(realmend, Text.size()-realmend) ); + Text = s; - CursorPos = realmbgn; - newMarkBegin = 0; - newMarkEnd = 0; - textChanged = true; - } - } - break; + CursorPos = realmbgn; + newMarkBegin = 0; + newMarkEnd = 0; + textChanged = true; + } + } + break; - case KEY_KEY_V: - if ( !isEnabled() ) - break; + case KEY_KEY_V: + if ( !isEnabled() ) + break; - // paste from the clipboard - if (Operator) - { - const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; - const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; + // paste from the clipboard + if (Operator) + { + const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; + const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; - // add new character - const c8* p = Operator->getTextFromClipboard(); - if (p) - { - if (MarkBegin == MarkEnd) - { - // insert text - core::stringw s = Text.subString(0, CursorPos); - s.append(p); - s.append( Text.subString(CursorPos, Text.size()-CursorPos) ); + // add new character + const c8* p = Operator->getTextFromClipboard(); + if (p) + { + if (MarkBegin == MarkEnd) + { + // insert text + core::stringw s = Text.subString(0, CursorPos); + s.append(p); + s.append( Text.subString(CursorPos, Text.size()-CursorPos) ); - if (!Max || s.size()<=Max) // thx to Fish FH for fix - { - Text = s; - s = p; - CursorPos += s.size(); - } - } - else - { - // replace text + if (!Max || s.size()<=Max) // thx to Fish FH for fix + { + Text = s; + s = p; + CursorPos += s.size(); + } + } + else + { + // replace text - core::stringw s = Text.subString(0, realmbgn); - s.append(p); - s.append( Text.subString(realmend, Text.size()-realmend) ); + core::stringw s = Text.subString(0, realmbgn); + s.append(p); + s.append( Text.subString(realmend, Text.size()-realmend) ); - if (!Max || s.size()<=Max) // thx to Fish FH for fix - { - Text = s; - s = p; - CursorPos = realmbgn + s.size(); - } - } - } + if (!Max || s.size()<=Max) // thx to Fish FH for fix + { + Text = s; + s = p; + CursorPos = realmbgn + s.size(); + } + } + } - newMarkBegin = 0; - newMarkEnd = 0; - textChanged = true; - } - break; - case KEY_HOME: + newMarkBegin = 0; + newMarkEnd = 0; + textChanged = true; + } + break; + case KEY_HOME: if (!m_rtl) { // move/highlight to start of text @@ -379,8 +379,8 @@ bool CGUIEditBox::processKey(const SEvent& event) newMarkEnd = 0; } } - break; - case KEY_END: + break; + case KEY_END: if (!m_rtl) { // move/highlight to end of text @@ -397,15 +397,15 @@ bool CGUIEditBox::processKey(const SEvent& event) newMarkEnd = 0; } } - break; - default: - return false; - } - } - // default keyboard handling - else - switch(event.KeyInput.Key) - { + break; + default: + return false; + } + } + // default keyboard handling + else + switch(event.KeyInput.Key) + { /* case KEY_KEY_Q: inputChar(L'\u05DC'); @@ -433,72 +433,72 @@ bool CGUIEditBox::processKey(const SEvent& event) return true; */ - case KEY_END: - if (!m_rtl) - { - s32 p = Text.size(); - if (WordWrap || MultiLine) - { - p = getLineFromPos(CursorPos); - p = BrokenTextPositions[p] + (s32)BrokenText[p].size(); - if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' )) - p-=1; - } - - if (event.KeyInput.Shift) - { - if (MarkBegin == MarkEnd) - newMarkBegin = CursorPos; - - newMarkEnd = p; - } - else - { - newMarkBegin = 0; - newMarkEnd = 0; - } - CursorPos = p; - BlinkStartTime = getTime(); - } - break; - case KEY_HOME: + case KEY_END: if (!m_rtl) - { + { + s32 p = Text.size(); + if (WordWrap || MultiLine) + { + p = getLineFromPos(CursorPos); + p = BrokenTextPositions[p] + (s32)BrokenText[p].size(); + if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' )) + p-=1; + } - s32 p = 0; - if (WordWrap || MultiLine) - { - p = getLineFromPos(CursorPos); - p = BrokenTextPositions[p]; - } + if (event.KeyInput.Shift) + { + if (MarkBegin == MarkEnd) + newMarkBegin = CursorPos; - if (event.KeyInput.Shift) - { - if (MarkBegin == MarkEnd) - newMarkBegin = CursorPos; - newMarkEnd = p; - } - else - { - newMarkBegin = 0; - newMarkEnd = 0; - } - CursorPos = p; - BlinkStartTime = getTime(); - } - break; - case KEY_RETURN: - if (MultiLine) - { - inputChar(L'\n'); - return true; - } - else - { - sendGuiEvent( EGET_EDITBOX_ENTER ); - } - break; - case KEY_LEFT: + newMarkEnd = p; + } + else + { + newMarkBegin = 0; + newMarkEnd = 0; + } + CursorPos = p; + BlinkStartTime = getTime(); + } + break; + case KEY_HOME: + if (!m_rtl) + { + + s32 p = 0; + if (WordWrap || MultiLine) + { + p = getLineFromPos(CursorPos); + p = BrokenTextPositions[p]; + } + + if (event.KeyInput.Shift) + { + if (MarkBegin == MarkEnd) + newMarkBegin = CursorPos; + newMarkEnd = p; + } + else + { + newMarkBegin = 0; + newMarkEnd = 0; + } + CursorPos = p; + BlinkStartTime = getTime(); + } + break; + case KEY_RETURN: + if (MultiLine) + { + inputChar(L'\n'); + return true; + } + else + { + sendGuiEvent( EGET_EDITBOX_ENTER ); + } + break; + case KEY_LEFT: if (!m_rtl) { if (event.KeyInput.Shift) @@ -520,9 +520,9 @@ bool CGUIEditBox::processKey(const SEvent& event) if (CursorPos > 0) CursorPos--; BlinkStartTime = getTime(); } - break; + break; - case KEY_RIGHT: + case KEY_RIGHT: if (!m_rtl) { if (event.KeyInput.Shift) @@ -544,309 +544,309 @@ bool CGUIEditBox::processKey(const SEvent& event) if (Text.size() > (u32)CursorPos) CursorPos++; BlinkStartTime = getTime(); } - break; - case KEY_UP: - if (MultiLine || (WordWrap && BrokenText.size() > 1) ) - { - s32 lineNo = getLineFromPos(CursorPos); - s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd); - if (lineNo > 0) - { - s32 cp = CursorPos - BrokenTextPositions[lineNo]; - if ((s32)BrokenText[lineNo-1].size() < cp) - CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1; - else - CursorPos = BrokenTextPositions[lineNo-1] + cp; - } + break; + case KEY_UP: + if (MultiLine || (WordWrap && BrokenText.size() > 1) ) + { + s32 lineNo = getLineFromPos(CursorPos); + s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd); + if (lineNo > 0) + { + s32 cp = CursorPos - BrokenTextPositions[lineNo]; + if ((s32)BrokenText[lineNo-1].size() < cp) + CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1; + else + CursorPos = BrokenTextPositions[lineNo-1] + cp; + } - if (event.KeyInput.Shift) - { - newMarkBegin = mb; - newMarkEnd = CursorPos; - } - else - { - newMarkBegin = 0; - newMarkEnd = 0; - } + if (event.KeyInput.Shift) + { + newMarkBegin = mb; + newMarkEnd = CursorPos; + } + else + { + newMarkBegin = 0; + newMarkEnd = 0; + } - } - else - { - return false; - } - break; - case KEY_DOWN: - if (MultiLine || (WordWrap && BrokenText.size() > 1) ) - { - s32 lineNo = getLineFromPos(CursorPos); - s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd); - if (lineNo < (s32)BrokenText.size()-1) - { - s32 cp = CursorPos - BrokenTextPositions[lineNo]; - if ((s32)BrokenText[lineNo+1].size() < cp) - CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1; - else - CursorPos = BrokenTextPositions[lineNo+1] + cp; - } + } + else + { + return false; + } + break; + case KEY_DOWN: + if (MultiLine || (WordWrap && BrokenText.size() > 1) ) + { + s32 lineNo = getLineFromPos(CursorPos); + s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd); + if (lineNo < (s32)BrokenText.size()-1) + { + s32 cp = CursorPos - BrokenTextPositions[lineNo]; + if ((s32)BrokenText[lineNo+1].size() < cp) + CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1; + else + CursorPos = BrokenTextPositions[lineNo+1] + cp; + } - if (event.KeyInput.Shift) - { - newMarkBegin = mb; - newMarkEnd = CursorPos; - } - else - { - newMarkBegin = 0; - newMarkEnd = 0; - } + if (event.KeyInput.Shift) + { + newMarkBegin = mb; + newMarkEnd = CursorPos; + } + else + { + newMarkBegin = 0; + newMarkEnd = 0; + } - } - else - { - return false; - } - break; + } + else + { + return false; + } + break; - case KEY_BACK: - if ( !isEnabled() ) - break; + case KEY_BACK: + if ( !isEnabled() ) + break; - if (Text.size()) - { - core::stringw s; + if (Text.size()) + { + core::stringw s; - if (MarkBegin != MarkEnd) - { - // delete marked text - const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; - const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; + if (MarkBegin != MarkEnd) + { + // delete marked text + const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; + const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; - s = Text.subString(0, realmbgn); - s.append( Text.subString(realmend, Text.size()-realmend) ); - Text = s; + s = Text.subString(0, realmbgn); + s.append( Text.subString(realmend, Text.size()-realmend) ); + Text = s; - CursorPos = realmbgn; - } - else - { - // delete text behind cursor - if (CursorPos>0) - s = Text.subString(0, CursorPos-1); - else - s = L""; - s.append( Text.subString(CursorPos, Text.size()-CursorPos) ); - Text = s; - --CursorPos; - } + CursorPos = realmbgn; + } + else + { + // delete text behind cursor + if (CursorPos>0) + s = Text.subString(0, CursorPos-1); + else + s = L""; + s.append( Text.subString(CursorPos, Text.size()-CursorPos) ); + Text = s; + --CursorPos; + } - if (CursorPos < 0) - CursorPos = 0; - BlinkStartTime = getTime(); - newMarkBegin = 0; - newMarkEnd = 0; - textChanged = true; - } - break; - case KEY_DELETE: - if ( !isEnabled() ) - break; + if (CursorPos < 0) + CursorPos = 0; + BlinkStartTime = getTime(); + newMarkBegin = 0; + newMarkEnd = 0; + textChanged = true; + } + break; + case KEY_DELETE: + if ( !isEnabled() ) + break; - if (Text.size() != 0) - { - core::stringw s; + if (Text.size() != 0) + { + core::stringw s; - if (MarkBegin != MarkEnd) - { - // delete marked text - const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; - const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; + if (MarkBegin != MarkEnd) + { + // delete marked text + const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; + const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; - s = Text.subString(0, realmbgn); - s.append( Text.subString(realmend, Text.size()-realmend) ); - Text = s; + s = Text.subString(0, realmbgn); + s.append( Text.subString(realmend, Text.size()-realmend) ); + Text = s; - CursorPos = realmbgn; - } - else - { - // delete text before cursor - s = Text.subString(0, CursorPos); - s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) ); - Text = s; - } + CursorPos = realmbgn; + } + else + { + // delete text before cursor + s = Text.subString(0, CursorPos); + s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) ); + Text = s; + } - if (CursorPos > (s32)Text.size()) - CursorPos = (s32)Text.size(); + if (CursorPos > (s32)Text.size()) + CursorPos = (s32)Text.size(); - BlinkStartTime = getTime(); - newMarkBegin = 0; - newMarkEnd = 0; - textChanged = true; - } - break; + BlinkStartTime = getTime(); + newMarkBegin = 0; + newMarkEnd = 0; + textChanged = true; + } + break; - case KEY_ESCAPE: - case KEY_TAB: - case KEY_SHIFT: - case KEY_F1: - case KEY_F2: - case KEY_F3: - case KEY_F4: - case KEY_F5: - case KEY_F6: - case KEY_F7: - case KEY_F8: - case KEY_F9: - case KEY_F10: - case KEY_F11: - case KEY_F12: - case KEY_F13: - case KEY_F14: - case KEY_F15: - case KEY_F16: - case KEY_F17: - case KEY_F18: - case KEY_F19: - case KEY_F20: - case KEY_F21: - case KEY_F22: - case KEY_F23: - case KEY_F24: - // ignore these keys - return false; + case KEY_ESCAPE: + case KEY_TAB: + case KEY_SHIFT: + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + case KEY_F13: + case KEY_F14: + case KEY_F15: + case KEY_F16: + case KEY_F17: + case KEY_F18: + case KEY_F19: + case KEY_F20: + case KEY_F21: + case KEY_F22: + case KEY_F23: + case KEY_F24: + // ignore these keys + return false; - default: - inputChar(event.KeyInput.Char); - return true; - } + default: + inputChar(event.KeyInput.Char); + return true; + } // Set new text markers setTextMarkers( newMarkBegin, newMarkEnd ); - // break the text if it has changed - if (textChanged) - { - breakText(); - sendGuiEvent(EGET_EDITBOX_CHANGED); - } + // break the text if it has changed + if (textChanged) + { + breakText(); + sendGuiEvent(EGET_EDITBOX_CHANGED); + } - calculateScrollPos(); + calculateScrollPos(); - return true; + return true; } //! draws the element and its children void CGUIEditBox::draw() { - if (!IsVisible) - return; + if (!IsVisible) + return; - const bool focus = Environment->hasFocus(this); + const bool focus = Environment->hasFocus(this); - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; + IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; - FrameRect = AbsoluteRect; + FrameRect = AbsoluteRect; - // draw the border + // draw the border - if (Border) - { - EGUI_DEFAULT_COLOR col = EGDC_GRAY_EDITABLE; - if ( isEnabled() ) - col = focus ? EGDC_FOCUSED_EDITABLE : EGDC_EDITABLE; - skin->draw3DSunkenPane(this, skin->getColor(col), - false, true, FrameRect, &AbsoluteClippingRect); + if (Border) + { + EGUI_DEFAULT_COLOR col = EGDC_GRAY_EDITABLE; + if ( isEnabled() ) + col = focus ? EGDC_FOCUSED_EDITABLE : EGDC_EDITABLE; + skin->draw3DSunkenPane(this, skin->getColor(col), + false, true, FrameRect, &AbsoluteClippingRect); - FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - } - core::rect localClipRect = FrameRect; - localClipRect.clipAgainst(AbsoluteClippingRect); + FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; + FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; + FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; + FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; + } + core::rect localClipRect = FrameRect; + localClipRect.clipAgainst(AbsoluteClippingRect); - // draw the text + // draw the text - IGUIFont* font = OverrideFont; - if (!OverrideFont) - font = skin->getFont(); + IGUIFont* font = OverrideFont; + if (!OverrideFont) + font = skin->getFont(); - s32 cursorLine = 0; - s32 charcursorpos = 0; + s32 cursorLine = 0; + s32 charcursorpos = 0; - if (font) - { - if (LastBreakFont != font) - { - breakText(); - } + if (font) + { + if (LastBreakFont != font) + { + breakText(); + } - // calculate cursor pos + // calculate cursor pos - core::stringw *txtLine = &Text; - s32 startPos = 0; + core::stringw *txtLine = &Text; + s32 startPos = 0; - core::stringw s, s2; + core::stringw s, s2; - // get mark position - const bool ml = (!PasswordBox && (WordWrap || MultiLine)); - const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; - const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; - const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0; - const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1; - const s32 lineCount = ml ? BrokenText.size() : 1; + // get mark position + const bool ml = (!PasswordBox && (WordWrap || MultiLine)); + const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; + const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; + const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0; + const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1; + const s32 lineCount = ml ? BrokenText.size() : 1; - // Save the override color information. - // Then, alter it if the edit box is disabled. - const bool prevOver = OverrideColorEnabled; - const video::SColor prevColor = OverrideColor; + // Save the override color information. + // Then, alter it if the edit box is disabled. + const bool prevOver = OverrideColorEnabled; + const video::SColor prevColor = OverrideColor; - if (Text.size()) - { - if (!isEnabled() && !OverrideColorEnabled) - { - OverrideColorEnabled = true; - OverrideColor = skin->getColor(EGDC_GRAY_TEXT); - } + if (Text.size()) + { + if (!isEnabled() && !OverrideColorEnabled) + { + OverrideColorEnabled = true; + OverrideColor = skin->getColor(EGDC_GRAY_TEXT); + } - for (s32 i=0; i < lineCount; ++i) - { - setTextRect(i); + for (s32 i=0; i < lineCount; ++i) + { + setTextRect(i); - // clipping test - don't draw anything outside the visible area - core::rect c = localClipRect; - c.clipAgainst(CurrentTextRect); - if (!c.isValid()) - continue; + // clipping test - don't draw anything outside the visible area + core::rect c = localClipRect; + c.clipAgainst(CurrentTextRect); + if (!c.isValid()) + continue; - // get current line - if (PasswordBox) - { - if (BrokenText.size() != 1) - { - BrokenText.clear(); - BrokenText.push_back(core::stringw()); - } - if (BrokenText[0].size() != Text.size()) - { - BrokenText[0] = Text; - for (u32 q = 0; q < Text.size(); ++q) - { - BrokenText[0] [q] = PasswordChar; - } - } - txtLine = &BrokenText[0]; - startPos = 0; - } - else - { - txtLine = ml ? &BrokenText[i] : &Text; - startPos = ml ? BrokenTextPositions[i] : 0; - } + // get current line + if (PasswordBox) + { + if (BrokenText.size() != 1) + { + BrokenText.clear(); + BrokenText.push_back(core::stringw()); + } + if (BrokenText[0].size() != Text.size()) + { + BrokenText[0] = Text; + for (u32 q = 0; q < Text.size(); ++q) + { + BrokenText[0] [q] = PasswordChar; + } + } + txtLine = &BrokenText[0]; + startPos = 0; + } + else + { + txtLine = ml ? &BrokenText[i] : &Text; + startPos = ml ? BrokenTextPositions[i] : 0; + } if (m_rtl) @@ -863,74 +863,74 @@ void CGUIEditBox::draw() false, true, &localClipRect); } - // draw mark and marked text - if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount) - { + // draw mark and marked text + if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount) + { - s32 mbegin = 0, mend = 0; - s32 lineStartPos = 0, lineEndPos = txtLine->size(); + s32 mbegin = 0, mend = 0; + s32 lineStartPos = 0, lineEndPos = txtLine->size(); - if (i == hlineStart) - { - // highlight start is on this line - s = txtLine->subString(0, realmbgn - startPos); - mbegin = font->getDimension(s.c_str()).Width; + if (i == hlineStart) + { + // highlight start is on this line + s = txtLine->subString(0, realmbgn - startPos); + mbegin = font->getDimension(s.c_str()).Width; - // deal with kerning - mbegin += font->getKerningWidth( - &((*txtLine)[realmbgn - startPos]), - realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0); + // deal with kerning + mbegin += font->getKerningWidth( + &((*txtLine)[realmbgn - startPos]), + realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0); - lineStartPos = realmbgn - startPos; - } - if (i == hlineStart + hlineCount - 1) - { - // highlight end is on this line - s2 = txtLine->subString(0, realmend - startPos); - mend = font->getDimension(s2.c_str()).Width; - lineEndPos = (s32)s2.size(); - } - else - mend = font->getDimension(txtLine->c_str()).Width; + lineStartPos = realmbgn - startPos; + } + if (i == hlineStart + hlineCount - 1) + { + // highlight end is on this line + s2 = txtLine->subString(0, realmend - startPos); + mend = font->getDimension(s2.c_str()).Width; + lineEndPos = (s32)s2.size(); + } + else + mend = font->getDimension(txtLine->c_str()).Width; - CurrentTextRect.UpperLeftCorner.X += mbegin; - CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin; + CurrentTextRect.UpperLeftCorner.X += mbegin; + CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin; - // draw mark - skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect); + // draw mark + skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect); - // draw marked text - s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos); + // draw marked text + s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos); - if (s.size()) - font->draw(s.c_str(), CurrentTextRect, - OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT), - false, true, &localClipRect); + if (s.size()) + font->draw(s.c_str(), CurrentTextRect, + OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT), + false, true, &localClipRect); - } - } + } + } - // Return the override color information to its previous settings. - OverrideColorEnabled = prevOver; - OverrideColor = prevColor; - } + // Return the override color information to its previous settings. + OverrideColorEnabled = prevOver; + OverrideColor = prevColor; + } - // draw cursor + // draw cursor - if (WordWrap || MultiLine) - { - cursorLine = getLineFromPos(CursorPos); - txtLine = &BrokenText[cursorLine]; - startPos = BrokenTextPositions[cursorLine]; - } - s = txtLine->subString(0,CursorPos-startPos); - charcursorpos = font->getDimension(s.c_str()).Width ; + if (WordWrap || MultiLine) + { + cursorLine = getLineFromPos(CursorPos); + txtLine = &BrokenText[cursorLine]; + startPos = BrokenTextPositions[cursorLine]; + } + s = txtLine->subString(0,CursorPos-startPos); + charcursorpos = font->getDimension(s.c_str()).Width ; // + font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0); - if (focus && (getTime() - BlinkStartTime) % 2 == 0 && !m_rtl) - { - //setTextRect(cursorLine); - //CurrentTextRect.UpperLeftCorner.X += charcursorpos; + if (focus && (getTime() - BlinkStartTime) % 2 == 0 && !m_rtl) + { + //setTextRect(cursorLine); + //CurrentTextRect.UpperLeftCorner.X += charcursorpos; setTextRect(0); @@ -940,26 +940,26 @@ void CGUIEditBox::draw() irr_driver->getVideoDriver()->draw2DRectangle( video::SColor(255,0,0,0), caret_rect ); /* - font->draw(L"_", CurrentTextRect, - OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT), - false, true, &localClipRect); + font->draw(L"_", CurrentTextRect, + OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT), + false, true, &localClipRect); */ - } - } + } + } - // draw children - IGUIElement::draw(); + // draw children + IGUIElement::draw(); } //! Sets the new caption of this element. void CGUIEditBox::setText(const wchar_t* text) { - Text = text; - if (u32(CursorPos) > Text.size()) - CursorPos = Text.size(); - HScrollPos = 0; - breakText(); + Text = text; + if (u32(CursorPos) > Text.size()) + CursorPos = Text.size(); + HScrollPos = 0; + breakText(); } @@ -967,7 +967,7 @@ void CGUIEditBox::setText(const wchar_t* text) //! \param enable: If set to true, the text will move around with the cursor position void CGUIEditBox::setAutoScroll(bool enable) { - AutoScroll = enable; + AutoScroll = enable; } @@ -975,8 +975,8 @@ void CGUIEditBox::setAutoScroll(bool enable) //! \return true if automatic scrolling is enabled, false if not bool CGUIEditBox::isAutoScrollEnabled() const { - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return AutoScroll; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return AutoScroll; } @@ -984,19 +984,19 @@ bool CGUIEditBox::isAutoScrollEnabled() const //! \return Returns the size in pixels of the text core::dimension2du CGUIEditBox::getTextDimension() { - core::rect ret; + core::rect ret; - setTextRect(0); - ret = CurrentTextRect; + setTextRect(0); + ret = CurrentTextRect; - for (u32 i=1; i < BrokenText.size(); ++i) - { - setTextRect(i); - ret.addInternalPoint(CurrentTextRect.UpperLeftCorner); - ret.addInternalPoint(CurrentTextRect.LowerRightCorner); - } + for (u32 i=1; i < BrokenText.size(); ++i) + { + setTextRect(i); + ret.addInternalPoint(CurrentTextRect.UpperLeftCorner); + ret.addInternalPoint(CurrentTextRect.LowerRightCorner); + } - return core::dimension2du(ret.getSize()); + return core::dimension2du(ret.getSize()); } @@ -1005,428 +1005,428 @@ core::dimension2du CGUIEditBox::getTextDimension() //! infinity. void CGUIEditBox::setMax(u32 max) { - Max = max; + Max = max; - if (Text.size() > Max && Max != 0) - Text = Text.subString(0, Max); + if (Text.size() > Max && Max != 0) + Text = Text.subString(0, Max); } //! Returns maximum amount of characters, previously set by setMax(); u32 CGUIEditBox::getMax() const { - return Max; + return Max; } bool CGUIEditBox::processMouse(const SEvent& event) { - switch(event.MouseInput.Event) - { - case irr::EMIE_LMOUSE_LEFT_UP: - if (Environment->hasFocus(this) && !m_rtl) - { - CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); - if (MouseMarking) - { - setTextMarkers( MarkBegin, CursorPos ); - } - MouseMarking = false; - calculateScrollPos(); - return true; - } - break; - case irr::EMIE_MOUSE_MOVED: - { - if (MouseMarking) - { - CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); - setTextMarkers( MarkBegin, CursorPos ); - calculateScrollPos(); - return true; - } - } - break; - case EMIE_LMOUSE_PRESSED_DOWN: - if (!Environment->hasFocus(this)) - { - BlinkStartTime = getTime(); - MouseMarking = true; - CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); - setTextMarkers(CursorPos, CursorPos ); - calculateScrollPos(); - return true; - } - else if (!m_rtl) - { - if (!AbsoluteClippingRect.isPointInside( - core::position2d(event.MouseInput.X, event.MouseInput.Y))) - { - return false; - } - else - { - // move cursor - CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); + switch(event.MouseInput.Event) + { + case irr::EMIE_LMOUSE_LEFT_UP: + if (Environment->hasFocus(this) && !m_rtl) + { + CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); + if (MouseMarking) + { + setTextMarkers( MarkBegin, CursorPos ); + } + MouseMarking = false; + calculateScrollPos(); + return true; + } + break; + case irr::EMIE_MOUSE_MOVED: + { + if (MouseMarking) + { + CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); + setTextMarkers( MarkBegin, CursorPos ); + calculateScrollPos(); + return true; + } + } + break; + case EMIE_LMOUSE_PRESSED_DOWN: + if (!Environment->hasFocus(this)) + { + BlinkStartTime = getTime(); + MouseMarking = true; + CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); + setTextMarkers(CursorPos, CursorPos ); + calculateScrollPos(); + return true; + } + else if (!m_rtl) + { + if (!AbsoluteClippingRect.isPointInside( + core::position2d(event.MouseInput.X, event.MouseInput.Y))) + { + return false; + } + else + { + // move cursor + CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); s32 newMarkBegin = MarkBegin; - if (!MouseMarking) - newMarkBegin = CursorPos; + if (!MouseMarking) + newMarkBegin = CursorPos; - MouseMarking = true; - setTextMarkers( newMarkBegin, CursorPos); - calculateScrollPos(); - return true; - } - } - default: - break; - } + MouseMarking = true; + setTextMarkers( newMarkBegin, CursorPos); + calculateScrollPos(); + return true; + } + } + default: + break; + } - return false; + return false; } s32 CGUIEditBox::getCursorPos(s32 x, s32 y) { - IGUIFont* font = OverrideFont; - IGUISkin* skin = Environment->getSkin(); - if (!OverrideFont) - font = skin->getFont(); + IGUIFont* font = OverrideFont; + IGUISkin* skin = Environment->getSkin(); + if (!OverrideFont) + font = skin->getFont(); - const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1; + const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1; - core::stringw *txtLine=0; - s32 startPos=0; - x+=3; + core::stringw *txtLine=0; + s32 startPos=0; + x+=3; - for (u32 i=0; i < lineCount; ++i) - { - setTextRect(i); - if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y) - y = CurrentTextRect.UpperLeftCorner.Y; - if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y ) - y = CurrentTextRect.LowerRightCorner.Y; + for (u32 i=0; i < lineCount; ++i) + { + setTextRect(i); + if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y) + y = CurrentTextRect.UpperLeftCorner.Y; + if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y ) + y = CurrentTextRect.LowerRightCorner.Y; - // is it inside this region? - if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y) - { - // we've found the clicked line - txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text; - startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0; - break; - } - } + // is it inside this region? + if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y) + { + // we've found the clicked line + txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text; + startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0; + break; + } + } - if (x < CurrentTextRect.UpperLeftCorner.X) - x = CurrentTextRect.UpperLeftCorner.X; + if (x < CurrentTextRect.UpperLeftCorner.X) + x = CurrentTextRect.UpperLeftCorner.X; - s32 idx = txtLine ? font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X) : -1; + s32 idx = txtLine ? font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X) : -1; - // click was on or left of the line - if (idx != -1) - return idx + startPos; + // click was on or left of the line + if (idx != -1) + return idx + startPos; - // click was off the right edge of the line, go to end. - return txtLine->size() + startPos; + // click was off the right edge of the line, go to end. + return txtLine->size() + startPos; } //! Breaks the single text line. void CGUIEditBox::breakText() { - IGUISkin* skin = Environment->getSkin(); + IGUISkin* skin = Environment->getSkin(); - if ((!WordWrap && !MultiLine) || !skin) - return; + if ((!WordWrap && !MultiLine) || !skin) + return; - BrokenText.clear(); // need to reallocate :/ - BrokenTextPositions.set_used(0); + BrokenText.clear(); // need to reallocate :/ + BrokenTextPositions.set_used(0); - IGUIFont* font = OverrideFont; - if (!OverrideFont) - font = skin->getFont(); + IGUIFont* font = OverrideFont; + if (!OverrideFont) + font = skin->getFont(); - if (!font) - return; + if (!font) + return; - LastBreakFont = font; + LastBreakFont = font; - core::stringw line; - core::stringw word; - core::stringw whitespace; - s32 lastLineStart = 0; - s32 size = Text.size(); - s32 length = 0; - s32 elWidth = RelativeRect.getWidth() - 6; - wchar_t c; + core::stringw line; + core::stringw word; + core::stringw whitespace; + s32 lastLineStart = 0; + s32 size = Text.size(); + s32 length = 0; + s32 elWidth = RelativeRect.getWidth() - 6; + wchar_t c; - for (s32 i=0; igetDimension(whitespace.c_str()).Width; - s32 worldlgth = font->getDimension(word.c_str()).Width; + if (c == L' ' || c == 0 || i == (size-1)) + { + if (word.size()) + { + // here comes the next whitespace, look if + // we can break the last word to the next line. + s32 whitelgth = font->getDimension(whitespace.c_str()).Width; + s32 worldlgth = font->getDimension(word.c_str()).Width; - if (WordWrap && length + worldlgth + whitelgth > elWidth) - { - // break to next line - length = worldlgth; - BrokenText.push_back(line); - BrokenTextPositions.push_back(lastLineStart); - lastLineStart = i - (s32)word.size(); - line = word; - } - else - { - // add word to line - line += whitespace; - line += word; - length += whitelgth + worldlgth; - } + if (WordWrap && length + worldlgth + whitelgth > elWidth) + { + // break to next line + length = worldlgth; + BrokenText.push_back(line); + BrokenTextPositions.push_back(lastLineStart); + lastLineStart = i - (s32)word.size(); + line = word; + } + else + { + // add word to line + line += whitespace; + line += word; + length += whitelgth + worldlgth; + } - word = L""; - whitespace = L""; - } + word = L""; + whitespace = L""; + } - whitespace += c; + whitespace += c; - // compute line break - if (lineBreak) - { - line += whitespace; - line += word; - BrokenText.push_back(line); - BrokenTextPositions.push_back(lastLineStart); - lastLineStart = i+1; - line = L""; - word = L""; - whitespace = L""; - length = 0; - } - } - else - { - // yippee this is a word.. - word += c; - } - } + // compute line break + if (lineBreak) + { + line += whitespace; + line += word; + BrokenText.push_back(line); + BrokenTextPositions.push_back(lastLineStart); + lastLineStart = i+1; + line = L""; + word = L""; + whitespace = L""; + length = 0; + } + } + else + { + // yippee this is a word.. + word += c; + } + } - line += whitespace; - line += word; - BrokenText.push_back(line); - BrokenTextPositions.push_back(lastLineStart); + line += whitespace; + line += word; + BrokenText.push_back(line); + BrokenTextPositions.push_back(lastLineStart); } void CGUIEditBox::setTextRect(s32 line) { - core::dimension2du d; + core::dimension2du d; - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; + IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; - IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont(); + IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont(); - if (!font) - return; + if (!font) + return; - // get text dimension - const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1; - if (WordWrap || MultiLine) - { - d = font->getDimension(BrokenText[line].c_str()); - } - else - { - d = font->getDimension(Text.c_str()); - d.Height = AbsoluteRect.getHeight(); - } - d.Height += font->getKerningHeight(); + // get text dimension + const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1; + if (WordWrap || MultiLine) + { + d = font->getDimension(BrokenText[line].c_str()); + } + else + { + d = font->getDimension(Text.c_str()); + d.Height = AbsoluteRect.getHeight(); + } + d.Height += font->getKerningHeight(); - // justification - switch (HAlign) - { - case EGUIA_CENTER: - // align to h centre - CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2); - CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2); - break; - case EGUIA_LOWERRIGHT: - // align to right edge - CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width; - CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth(); - break; - default: - // align to left edge - CurrentTextRect.UpperLeftCorner.X = 0; - CurrentTextRect.LowerRightCorner.X = d.Width; + // justification + switch (HAlign) + { + case EGUIA_CENTER: + // align to h centre + CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2); + CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2); + break; + case EGUIA_LOWERRIGHT: + // align to right edge + CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width; + CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth(); + break; + default: + // align to left edge + CurrentTextRect.UpperLeftCorner.X = 0; + CurrentTextRect.LowerRightCorner.X = d.Width; - } + } - switch (VAlign) - { - case EGUIA_CENTER: - // align to v centre - CurrentTextRect.UpperLeftCorner.Y = - (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line; - break; - case EGUIA_LOWERRIGHT: - // align to bottom edge - CurrentTextRect.UpperLeftCorner.Y = - FrameRect.getHeight() - lineCount*d.Height + d.Height*line; - break; - default: - // align to top edge - CurrentTextRect.UpperLeftCorner.Y = d.Height*line; - break; - } + switch (VAlign) + { + case EGUIA_CENTER: + // align to v centre + CurrentTextRect.UpperLeftCorner.Y = + (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line; + break; + case EGUIA_LOWERRIGHT: + // align to bottom edge + CurrentTextRect.UpperLeftCorner.Y = + FrameRect.getHeight() - lineCount*d.Height + d.Height*line; + break; + default: + // align to top edge + CurrentTextRect.UpperLeftCorner.Y = d.Height*line; + break; + } - CurrentTextRect.UpperLeftCorner.X -= HScrollPos; - CurrentTextRect.LowerRightCorner.X -= HScrollPos; - CurrentTextRect.UpperLeftCorner.Y -= VScrollPos; - CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height; + CurrentTextRect.UpperLeftCorner.X -= HScrollPos; + CurrentTextRect.LowerRightCorner.X -= HScrollPos; + CurrentTextRect.UpperLeftCorner.Y -= VScrollPos; + CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height; - CurrentTextRect += FrameRect.UpperLeftCorner; + CurrentTextRect += FrameRect.UpperLeftCorner; } s32 CGUIEditBox::getLineFromPos(s32 pos) { - if (!WordWrap && !MultiLine) - return 0; + if (!WordWrap && !MultiLine) + return 0; - s32 i=0; - while (i < (s32)BrokenTextPositions.size()) - { - if (BrokenTextPositions[i] > pos) - return i-1; - ++i; - } - return (s32)BrokenTextPositions.size() - 1; + s32 i=0; + while (i < (s32)BrokenTextPositions.size()) + { + if (BrokenTextPositions[i] > pos) + return i-1; + ++i; + } + return (s32)BrokenTextPositions.size() - 1; } void CGUIEditBox::inputChar(wchar_t c) { - if (!isEnabled()) - return; + if (!isEnabled()) + return; - if (c != 0) - { - if (Text.size() < Max || Max == 0) - { - core::stringw s; + if (c != 0) + { + if (Text.size() < Max || Max == 0) + { + core::stringw s; - if (MarkBegin != MarkEnd) - { - // replace marked text - const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; - const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; + if (MarkBegin != MarkEnd) + { + // replace marked text + const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; + const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; - s = Text.subString(0, realmbgn); - s.append(c); - s.append( Text.subString(realmend, Text.size()-realmend) ); - Text = s; - CursorPos = realmbgn+1; - } - else - { - // add new character - s = Text.subString(0, CursorPos); - s.append(c); - s.append( Text.subString(CursorPos, Text.size()-CursorPos) ); - Text = s; - ++CursorPos; - } + s = Text.subString(0, realmbgn); + s.append(c); + s.append( Text.subString(realmend, Text.size()-realmend) ); + Text = s; + CursorPos = realmbgn+1; + } + else + { + // add new character + s = Text.subString(0, CursorPos); + s.append(c); + s.append( Text.subString(CursorPos, Text.size()-CursorPos) ); + Text = s; + ++CursorPos; + } - BlinkStartTime = getTime(); - setTextMarkers(0, 0); - } - } - breakText(); - sendGuiEvent(EGET_EDITBOX_CHANGED); - calculateScrollPos(); + BlinkStartTime = getTime(); + setTextMarkers(0, 0); + } + } + breakText(); + sendGuiEvent(EGET_EDITBOX_CHANGED); + calculateScrollPos(); } void CGUIEditBox::calculateScrollPos() { - if (!AutoScroll) - return; + if (!AutoScroll) + return; - // calculate horizontal scroll position - s32 cursLine = getLineFromPos(CursorPos); - setTextRect(cursLine); + // calculate horizontal scroll position + s32 cursLine = getLineFromPos(CursorPos); + setTextRect(cursLine); - // don't do horizontal scrolling when wordwrap is enabled. - if (!WordWrap) - { - // get cursor position - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont(); - if (!font) - return; + // don't do horizontal scrolling when wordwrap is enabled. + if (!WordWrap) + { + // get cursor position + IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; + IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont(); + if (!font) + return; - core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text; - s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos; + core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text; + s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos; - s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos + - font->getDimension(txtLine->subString(0, cPos).c_str()).Width; + s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos + + font->getDimension(txtLine->subString(0, cPos).c_str()).Width; - s32 cEnd = cStart + font->getDimension(L"_ ").Width; + s32 cEnd = cStart + font->getDimension(L"_ ").Width; - if (FrameRect.LowerRightCorner.X < cEnd) - HScrollPos = cEnd - FrameRect.LowerRightCorner.X; - else if (FrameRect.UpperLeftCorner.X > cStart) - HScrollPos = cStart - FrameRect.UpperLeftCorner.X; - else - HScrollPos = 0; + if (FrameRect.LowerRightCorner.X < cEnd) + HScrollPos = cEnd - FrameRect.LowerRightCorner.X; + else if (FrameRect.UpperLeftCorner.X > cStart) + HScrollPos = cStart - FrameRect.UpperLeftCorner.X; + else + HScrollPos = 0; - // todo: adjust scrollbar - } + // todo: adjust scrollbar + } - // vertical scroll position - if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos) - VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos; + // vertical scroll position + if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos) + VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos; - else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos) - VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos; - else - VScrollPos = 0; + else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos) + VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos; + else + VScrollPos = 0; - // todo: adjust scrollbar + // todo: adjust scrollbar } //! set text markers @@ -1443,8 +1443,8 @@ void CGUIEditBox::setTextMarkers(s32 begin, s32 end) //! send some gui event to parent void CGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type) { - if ( Parent ) - { + if ( Parent ) + { SEvent e; e.EventType = EET_GUI_EVENT; e.GUIEvent.Caller = this; @@ -1452,53 +1452,53 @@ void CGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type) e.GUIEvent.EventType = type; Parent->OnEvent(e); - } + } } //! Writes attributes of the element. void CGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const { - // IGUIEditBox::serializeAttributes(out,options); + // IGUIEditBox::serializeAttributes(out,options); - out->addBool ("OverrideColorEnabled",OverrideColorEnabled ); - out->addColor ("OverrideColor", OverrideColor); - // out->addFont("OverrideFont",OverrideFont); - out->addInt ("MaxChars", Max); - out->addBool ("WordWrap", WordWrap); - out->addBool ("MultiLine", MultiLine); - out->addBool ("AutoScroll", AutoScroll); - out->addBool ("PasswordBox", PasswordBox); - core::stringw ch = L" "; - ch[0] = PasswordChar; - out->addString("PasswordChar", ch.c_str()); - out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); - out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); + out->addBool ("OverrideColorEnabled",OverrideColorEnabled ); + out->addColor ("OverrideColor", OverrideColor); + // out->addFont("OverrideFont",OverrideFont); + out->addInt ("MaxChars", Max); + out->addBool ("WordWrap", WordWrap); + out->addBool ("MultiLine", MultiLine); + out->addBool ("AutoScroll", AutoScroll); + out->addBool ("PasswordBox", PasswordBox); + core::stringw ch = L" "; + ch[0] = PasswordChar; + out->addString("PasswordChar", ch.c_str()); + out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); + out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); - IGUIEditBox::serializeAttributes(out,options); + IGUIEditBox::serializeAttributes(out,options); } //! Reads attributes of the element void CGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) { - IGUIEditBox::deserializeAttributes(in,options); + IGUIEditBox::deserializeAttributes(in,options); - setOverrideColor(in->getAttributeAsColor("OverrideColor")); - enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled")); - setMax(in->getAttributeAsInt("MaxChars")); - setWordWrap(in->getAttributeAsBool("WordWrap")); - setMultiLine(in->getAttributeAsBool("MultiLine")); - setAutoScroll(in->getAttributeAsBool("AutoScroll")); - core::stringw ch = in->getAttributeAsStringW("PasswordChar"); + setOverrideColor(in->getAttributeAsColor("OverrideColor")); + enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled")); + setMax(in->getAttributeAsInt("MaxChars")); + setWordWrap(in->getAttributeAsBool("WordWrap")); + setMultiLine(in->getAttributeAsBool("MultiLine")); + setAutoScroll(in->getAttributeAsBool("AutoScroll")); + core::stringw ch = in->getAttributeAsStringW("PasswordChar"); - if (!ch.size()) - setPasswordBox(in->getAttributeAsBool("PasswordBox")); - else - setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]); + if (!ch.size()) + setPasswordBox(in->getAttributeAsBool("PasswordBox")); + else + setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]); - setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames), - (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames)); + setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames), + (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames)); - // setOverrideFont(in->getAttributeAsFont("OverrideFont")); + // setOverrideFont(in->getAttributeAsFont("OverrideFont")); } diff --git a/src/guiengine/widgets/CGUISTKListBox.cpp b/src/guiengine/widgets/CGUISTKListBox.cpp new file mode 100644 index 000000000..8689b12bd --- /dev/null +++ b/src/guiengine/widgets/CGUISTKListBox.cpp @@ -0,0 +1,757 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// 2013 Glenn De Jonghe +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "guiengine/widgets/CGUISTKListBox.h" + +#include "IGUISkin.h" +#include "IGUIEnvironment.h" +#include "IVideoDriver.h" +#include "IGUIFont.h" +#include "IGUISpriteBank.h" +#include "IGUIScrollBar.h" +#include "utils/time.hpp" + + +namespace irr +{ +namespace gui +{ + +//! constructor +CGUISTKListBox::CGUISTKListBox(IGUIEnvironment* environment, IGUIElement* parent, + s32 id, core::rect rectangle, bool clip, + bool drawBack, bool moveOverSelect) +: IGUIElement(EGUIET_LIST_BOX, environment, parent, id, rectangle), Selected(-1), + ItemHeight(0),ItemHeightOverride(0), + TotalItemHeight(0), ItemsIconWidth(0), Font(0), IconBank(0), + ScrollBar(0), selectTime(0), LastKeyTime(0), Selecting(false), DrawBack(drawBack), + MoveOverSelect(moveOverSelect), AutoScroll(true), HighlightWhenNotFocused(true) +{ + #ifdef _DEBUG + setDebugName("CGUISTKListBox"); + #endif + + IGUISkin* skin = Environment->getSkin(); + const s32 s = skin->getSize(EGDS_SCROLLBAR_SIZE); + + ScrollBar = Environment->addScrollBar(false, + core::rect(RelativeRect.getWidth() - s, 0, + RelativeRect.getWidth(), RelativeRect.getHeight()), this, -1); + ScrollBar->grab(); + ScrollBar->setSubElement(true); + ScrollBar->setTabStop(false); + ScrollBar->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT); + ScrollBar->setVisible(false); + ScrollBar->setPos(0); + + setNotClipped(!clip); + + // this element can be tabbed to + setTabStop(true); + setTabOrder(-1); + + updateAbsolutePosition(); +} + + +//! destructor +CGUISTKListBox::~CGUISTKListBox() +{ + if (ScrollBar) + ScrollBar->drop(); + + if (Font) + Font->drop(); + + if (IconBank) + IconBank->drop(); +} + + +//! returns amount of list items +u32 CGUISTKListBox::getItemCount() const +{ + return Items.size(); +} + + + +const wchar_t* CGUISTKListBox::getCellText(u32 row_num, u32 col_num) const +{ + if ( row_num >= Items.size() ) + return 0; + if ( col_num >= Items[row_num].m_contents.size() ) + return 0; + return Items[row_num].m_contents[col_num].m_text.c_str(); +} + +CGUISTKListBox::ListItem CGUISTKListBox::getItem(u32 id) const +{ + return Items[id]; +} + + +//! Returns the icon of an item +s32 CGUISTKListBox::getIcon(u32 row_num, u32 col_num) const +{ + if ( row_num >= Items.size() ) + return -1; + if ( col_num >= Items[row_num].m_contents.size() ) + return -1; + return Items[row_num].m_contents[col_num].m_icon; +} + +void CGUISTKListBox::removeItem(u32 id) +{ + if (id >= Items.size()) + return; + + if ((u32)Selected==id) + { + Selected = -1; + } + else if ((u32)Selected > id) + { + Selected -= 1; + selectTime = (u32)StkTime::getTimeSinceEpoch(); + } + + Items.erase(id); + + recalculateItemHeight(); +} + + +s32 CGUISTKListBox::getItemAt(s32 xpos, s32 ypos) const +{ + if ( xpos < AbsoluteRect.UpperLeftCorner.X || xpos >= AbsoluteRect.LowerRightCorner.X + || ypos < AbsoluteRect.UpperLeftCorner.Y || ypos >= AbsoluteRect.LowerRightCorner.Y + ) + return -1; + + if ( ItemHeight == 0 ) + return -1; + + s32 item = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight; + if ( item < 0 || item >= (s32)Items.size()) + return -1; + + return item; +} + +//! clears the list +void CGUISTKListBox::clear() +{ + Items.clear(); + ItemsIconWidth = 0; + Selected = -1; + + if (ScrollBar) + ScrollBar->setPos(0); + + recalculateItemHeight(); +} + + +void CGUISTKListBox::recalculateItemHeight() +{ + IGUISkin* skin = Environment->getSkin(); + + if (Font != skin->getFont()) + { + if (Font) + Font->drop(); + + Font = skin->getFont(); + if ( 0 == ItemHeightOverride ) + ItemHeight = 0; + + if (Font) + { + if ( 0 == ItemHeightOverride ) + ItemHeight = Font->getDimension(L"A").Height + 4; + + Font->grab(); + } + } + + TotalItemHeight = ItemHeight * Items.size(); + ScrollBar->setMax( core::max_(0, TotalItemHeight - AbsoluteRect.getHeight()) ); + s32 minItemHeight = ItemHeight > 0 ? ItemHeight : 1; + ScrollBar->setSmallStep ( minItemHeight ); + ScrollBar->setLargeStep ( 2*minItemHeight ); + + if ( TotalItemHeight <= AbsoluteRect.getHeight() ) + ScrollBar->setVisible(false); + else + ScrollBar->setVisible(true); +} + + +//! returns id of selected item. returns -1 if no item is selected. +s32 CGUISTKListBox::getSelected() const +{ + return Selected; +} + + +//! sets the selected item. Set this to -1 if no item should be selected +void CGUISTKListBox::setSelected(s32 id) +{ + if ((u32)id>=Items.size()) + Selected = -1; + else + Selected = id; + + selectTime = (u32)StkTime::getTimeSinceEpoch(); + + recalculateScrollPos(); +} + +s32 CGUISTKListBox::getRowByCellText(const wchar_t * text) +{ + s32 row_index = -1; + s32 col_index = -1; + if (text) + { + for ( row_index = 0; row_index < (s32) Items.size(); ++row_index ) + { + for ( col_index = 0; col_index < (s32) Items[row_index].m_contents.size(); ++col_index ) + { + if ( Items[row_index].m_contents[col_index].m_text == text ) return row_index; + } + } + } + return -1; +} + +//! sets the selected item. Set this to -1 if no item should be selected +void CGUISTKListBox::setSelectedByCellText(const wchar_t * text) +{ + setSelected(getRowByCellText(text)); +} + +s32 CGUISTKListBox::getRowByInternalName(const std::string & text) const +{ + s32 row_index = -1; + if (text != "") + { + for ( row_index = 0; row_index < (s32) Items.size(); ++row_index ) + { + if (Items[row_index].m_internal_name == text) return row_index; + } + } + return -1; +} + +//! called if an event happened. +bool CGUISTKListBox::OnEvent(const SEvent& event) +{ + if (isEnabled()) + { + switch(event.EventType) + { + case EET_KEY_INPUT_EVENT: + if (event.KeyInput.PressedDown && + (event.KeyInput.Key == KEY_DOWN || + event.KeyInput.Key == KEY_UP || + event.KeyInput.Key == KEY_HOME || + event.KeyInput.Key == KEY_END || + event.KeyInput.Key == KEY_NEXT || + event.KeyInput.Key == KEY_PRIOR ) ) + { + s32 oldSelected = Selected; + switch (event.KeyInput.Key) + { + case KEY_DOWN: + Selected += 1; + break; + case KEY_UP: + Selected -= 1; + break; + case KEY_HOME: + Selected = 0; + break; + case KEY_END: + Selected = (s32)Items.size()-1; + break; + case KEY_NEXT: + Selected += AbsoluteRect.getHeight() / ItemHeight; + break; + case KEY_PRIOR: + Selected -= AbsoluteRect.getHeight() / ItemHeight; + break; + default: + break; + } + if (Selected >= (s32)Items.size()) + Selected = Items.size() - 1; + else + if (Selected<0) + Selected = 0; + + recalculateScrollPos(); + + // post the news + + if (oldSelected != Selected && Parent && !Selecting && !MoveOverSelect) + { + SEvent e; + e.EventType = EET_GUI_EVENT; + e.GUIEvent.Caller = this; + e.GUIEvent.Element = 0; + e.GUIEvent.EventType = EGET_LISTBOX_CHANGED; + Parent->OnEvent(e); + } + + return true; + } + else + if (!event.KeyInput.PressedDown && ( event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE ) ) + { + if (Parent) + { + SEvent e; + e.EventType = EET_GUI_EVENT; + e.GUIEvent.Caller = this; + e.GUIEvent.Element = 0; + e.GUIEvent.EventType = EGET_LISTBOX_SELECTED_AGAIN; + Parent->OnEvent(e); + } + return true; + } + break; + + case EET_GUI_EVENT: + switch(event.GUIEvent.EventType) + { + case gui::EGET_SCROLL_BAR_CHANGED: + if (event.GUIEvent.Caller == ScrollBar) + return true; + break; + case gui::EGET_ELEMENT_FOCUS_LOST: + { + if (event.GUIEvent.Caller == this) + Selecting = false; + break; + } + + default: + break; + } + break; + + case EET_MOUSE_INPUT_EVENT: + { + core::position2d p(event.MouseInput.X, event.MouseInput.Y); + + switch(event.MouseInput.Event) + { + case EMIE_MOUSE_WHEEL: + ScrollBar->setPos(ScrollBar->getPos() + (event.MouseInput.Wheel < 0 ? -1 : 1)*-ItemHeight/2); + return true; + + case EMIE_LMOUSE_PRESSED_DOWN: + { + Selecting = true; + return true; + } + + case EMIE_LMOUSE_LEFT_UP: + { + Selecting = false; + + if (isPointInside(p)) + selectNew(event.MouseInput.Y); + + return true; + } + + case EMIE_MOUSE_MOVED: + if (Selecting || MoveOverSelect) + { + if (isPointInside(p)) + { + selectNew(event.MouseInput.Y, true); + return true; + } + } + default: + break; + } + } + break; + case EET_LOG_TEXT_EVENT: + case EET_USER_EVENT: + case EET_JOYSTICK_INPUT_EVENT: + case EGUIET_FORCE_32_BIT: + break; + } + } + + return IGUIElement::OnEvent(event); +} + + +void CGUISTKListBox::selectNew(s32 ypos, bool onlyHover) +{ + u32 now = (u32)StkTime::getTimeSinceEpoch(); + s32 oldSelected = Selected; + + Selected = getItemAt(AbsoluteRect.UpperLeftCorner.X, ypos); + if (Selected<0 && !Items.empty()) + Selected = 0; + + recalculateScrollPos(); + + gui::EGUI_EVENT_TYPE eventType = (Selected == oldSelected && now < selectTime + 500) ? EGET_LISTBOX_SELECTED_AGAIN : EGET_LISTBOX_CHANGED; + selectTime = now; + // post the news + if (Parent && !onlyHover) + { + SEvent event; + event.EventType = EET_GUI_EVENT; + event.GUIEvent.Caller = this; + event.GUIEvent.Element = 0; + event.GUIEvent.EventType = eventType; + Parent->OnEvent(event); + } +} + + +//! Update the position and size of the listbox, and update the scrollbar +void CGUISTKListBox::updateAbsolutePosition() +{ + IGUIElement::updateAbsolutePosition(); + + recalculateItemHeight(); +} + + +//! draws the element and its children +void CGUISTKListBox::draw() +{ + if (!IsVisible) + return; + + recalculateItemHeight(); // if the font changed + + IGUISkin* skin = Environment->getSkin(); + + core::rect* clipRect = 0; + + // draw background + core::rect frameRect(AbsoluteRect); + + // draw items + + core::rect clientClip(AbsoluteRect); + clientClip.UpperLeftCorner.Y += 1; + clientClip.UpperLeftCorner.X += 1; + if (ScrollBar->isVisible()) + clientClip.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE); + clientClip.LowerRightCorner.Y -= 1; + clientClip.clipAgainst(AbsoluteClippingRect); + + skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT), true, + DrawBack, frameRect, &clientClip); + + if (clipRect) + clientClip.clipAgainst(*clipRect); + + frameRect = AbsoluteRect; + frameRect.UpperLeftCorner.X += 1; + if (ScrollBar->isVisible()) + frameRect.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE); + + frameRect.LowerRightCorner.Y = AbsoluteRect.UpperLeftCorner.Y + ItemHeight; + + frameRect.UpperLeftCorner.Y -= ScrollBar->getPos(); + frameRect.LowerRightCorner.Y -= ScrollBar->getPos(); + + bool hl = (HighlightWhenNotFocused || Environment->hasFocus(this) || Environment->hasFocus(ScrollBar)); + + for (s32 i=0; i<(s32)Items.size(); ++i) + { + if (frameRect.LowerRightCorner.Y >= AbsoluteRect.UpperLeftCorner.Y && + frameRect.UpperLeftCorner.Y <= AbsoluteRect.LowerRightCorner.Y) + { + if (i == Selected && hl) + skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), frameRect, &clientClip); + + core::rect textRect = frameRect; + + if (Font) + { + int total_proportion = 0; + for(unsigned int x = 0; x < Items[i].m_contents.size(); ++x) + { + total_proportion += Items[i].m_contents[x].m_proportion; + } + int part_size = (int)(textRect.getWidth() / float(total_proportion)); + + for(unsigned int x = 0; x < Items[i].m_contents.size(); ++x) + { + textRect.LowerRightCorner.X = textRect.UpperLeftCorner.X + + (Items[i].m_contents[x].m_proportion * part_size); + textRect.UpperLeftCorner.X += 3; + + if (IconBank && (Items[i].m_contents[x].m_icon > -1)) + { + core::position2di iconPos = textRect.UpperLeftCorner; + iconPos.Y += textRect.getHeight() / 2; + iconPos.X += ItemsIconWidth/2; + + if ( i==Selected && hl ) + { + IconBank->draw2DSprite( + (u32)Items[i].m_contents[x].m_icon, + iconPos, &clientClip, + hasItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) ? + getItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_ICON_HIGHLIGHT), + selectTime, (u32)StkTime::getTimeSinceEpoch(), false, true); + } + else + { + IconBank->draw2DSprite( + (u32)Items[i].m_contents[x].m_icon, + iconPos, + &clientClip, + hasItemOverrideColor(i, EGUI_LBC_ICON) ? getItemOverrideColor(i, EGUI_LBC_ICON) : getItemDefaultColor(EGUI_LBC_ICON), + 0 , (i==Selected) ? (u32)StkTime::getTimeSinceEpoch() : 0, false, true); + } + textRect.UpperLeftCorner.X += ItemsIconWidth; + } + + textRect.UpperLeftCorner.X += 3; + + if ( i==Selected && hl ) + { + Font->draw( + Items[i].m_contents[x].m_text.c_str(), + textRect, + hasItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) ? + getItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_TEXT_HIGHLIGHT), + Items[i].m_contents[x].m_center, true, &clientClip); + } + else + { + Font->draw( + Items[i].m_contents[x].m_text.c_str(), + textRect, + hasItemOverrideColor(i, EGUI_LBC_TEXT) ? getItemOverrideColor(i, EGUI_LBC_TEXT) : getItemDefaultColor(EGUI_LBC_TEXT), + Items[i].m_contents[x].m_center, true, &clientClip); + } + //Position back to inital pos + textRect.UpperLeftCorner.X -= ItemsIconWidth+6; + //Calculate new beginning + textRect.UpperLeftCorner.X += Items[i].m_contents[x].m_proportion * part_size; + } + } + } + + frameRect.UpperLeftCorner.Y += ItemHeight; + frameRect.LowerRightCorner.Y += ItemHeight; + } + + IGUIElement::draw(); +} + + +//! adds an list item with an icon +u32 CGUISTKListBox::addItem(const ListItem & item) +{ + Items.push_back(item); + recalculateItemHeight(); + recalculateIconWidth(); + return Items.size() - 1; +} + + +void CGUISTKListBox::setSpriteBank(IGUISpriteBank* bank) +{ + if ( bank == IconBank ) + return; + if (IconBank) + IconBank->drop(); + + IconBank = bank; + if (IconBank) + IconBank->grab(); +} + + +void CGUISTKListBox::recalculateScrollPos() +{ + if (!AutoScroll) + return; + + const s32 selPos = (Selected == -1 ? TotalItemHeight : Selected * ItemHeight) - ScrollBar->getPos(); + + if (selPos < 0) + { + ScrollBar->setPos(ScrollBar->getPos() + selPos); + } + else + if (selPos > AbsoluteRect.getHeight() - ItemHeight) + { + ScrollBar->setPos(ScrollBar->getPos() + selPos - AbsoluteRect.getHeight() + ItemHeight); + } +} + + +void CGUISTKListBox::setAutoScrollEnabled(bool scroll) +{ + AutoScroll = scroll; +} + + +bool CGUISTKListBox::isAutoScrollEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return AutoScroll; +} + +void CGUISTKListBox::recalculateIconWidth() +{ + for(int x = 0; x < (int)Items.getLast().m_contents.size(); ++x) + { + s32 icon = Items.getLast().m_contents[x].m_icon; + if (IconBank && icon > -1 && + IconBank->getSprites().size() > (u32)icon && + IconBank->getSprites()[(u32)icon].Frames.size()) + { + u32 rno = IconBank->getSprites()[(u32)icon].Frames[0].rectNumber; + if (IconBank->getPositions().size() > rno) + { + const s32 w = IconBank->getPositions()[rno].getWidth(); + if (w > ItemsIconWidth) + ItemsIconWidth = w; + } + } + } +} + + +void CGUISTKListBox::setCell(u32 row_num, u32 col_num, const wchar_t* text, s32 icon) +{ + if ( row_num >= Items.size() ) + return; + if ( col_num >= Items[row_num].m_contents.size() ) + return; + Items[row_num].m_contents[col_num].m_text = text; + Items[row_num].m_contents[col_num].m_icon = icon; + + recalculateItemHeight(); + recalculateIconWidth(); +} + +void CGUISTKListBox::swapItems(u32 index1, u32 index2) +{ + if ( index1 >= Items.size() || index2 >= Items.size() ) + return; + + ListItem dummmy = Items[index1]; + Items[index1] = Items[index2]; + Items[index2] = dummmy; +} + + +void CGUISTKListBox::setItemOverrideColor(u32 index, video::SColor color) +{ + for ( u32 c=0; c < EGUI_LBC_COUNT; ++c ) + { + Items[index].OverrideColors[c].Use = true; + Items[index].OverrideColors[c].Color = color; + } +} + + +void CGUISTKListBox::setItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType, video::SColor color) +{ + if ( index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT ) + return; + + Items[index].OverrideColors[colorType].Use = true; + Items[index].OverrideColors[colorType].Color = color; +} + + +void CGUISTKListBox::clearItemOverrideColor(u32 index) +{ + for (u32 c=0; c < (u32)EGUI_LBC_COUNT; ++c ) + { + Items[index].OverrideColors[c].Use = false; + } +} + + +void CGUISTKListBox::clearItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) +{ + if ( index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT ) + return; + + Items[index].OverrideColors[colorType].Use = false; +} + + +bool CGUISTKListBox::hasItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const +{ + if ( index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT ) + return false; + + return Items[index].OverrideColors[colorType].Use; +} + + +video::SColor CGUISTKListBox::getItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const +{ + if ( (u32)index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT ) + return video::SColor(); + + return Items[index].OverrideColors[colorType].Color; +} + + +video::SColor CGUISTKListBox::getItemDefaultColor(EGUI_LISTBOX_COLOR colorType) const +{ + IGUISkin* skin = Environment->getSkin(); + if ( !skin ) + return video::SColor(); + + switch ( colorType ) + { + case EGUI_LBC_TEXT: + return skin->getColor(EGDC_BUTTON_TEXT); + case EGUI_LBC_TEXT_HIGHLIGHT: + return skin->getColor(EGDC_HIGH_LIGHT_TEXT); + case EGUI_LBC_ICON: + return skin->getColor(EGDC_ICON); + case EGUI_LBC_ICON_HIGHLIGHT: + return skin->getColor(EGDC_ICON_HIGH_LIGHT); + default: + return video::SColor(); + } +} + +//! set global itemHeight +void CGUISTKListBox::setItemHeight( s32 height ) +{ + ItemHeight = height; + ItemHeightOverride = 1; +} + + +//! Sets whether to draw the background +void CGUISTKListBox::setDrawBackground(bool draw) +{ + DrawBack = draw; +} + + +} // end namespace gui +} // end namespace irr + + diff --git a/src/guiengine/widgets/CGUISTKListBox.h b/src/guiengine/widgets/CGUISTKListBox.h new file mode 100644 index 000000000..444b69c92 --- /dev/null +++ b/src/guiengine/widgets/CGUISTKListBox.h @@ -0,0 +1,198 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// 2013 Glenn De Jonghe +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef HEADER_CGUISTKListBox_HPP +#define HEADER_CGUISTKListBox_HPP + +#include "IrrCompileConfig.h" + +#include "IGUIListBox.h" +#include "IGUIElement.h" +#include "irrArray.h" +#include + +namespace irr +{ + namespace gui + { + class IGUIFont; + class IGUIScrollBar; + + class CGUISTKListBox : public IGUIElement + { + public: + + struct ListItem + { + + struct ListCell + { + irr::core::stringw m_text; + int m_proportion; + s32 m_icon; + bool m_center; + + ListCell(irr::core::stringw text, s32 icon = -1, int proportion = 1, bool center = false) + { + m_text = text; + m_proportion = proportion; + m_icon = icon; + m_center = center; + } + }; + + core::array< ListCell > m_contents; + + // Actually only used in list_widget -- still refactoring FIXME + std::string m_internal_name; + int m_current_id; + + // A multicolor extension + struct ListItemOverrideColor + { + ListItemOverrideColor() : Use(false) {} + bool Use; + video::SColor Color; + }; + + ListItemOverrideColor OverrideColors[EGUI_LBC_COUNT]; + }; + + //! constructor + CGUISTKListBox(IGUIEnvironment* environment, IGUIElement* parent, + s32 id, core::rect rectangle, bool clip=true, + bool drawBack=false, bool moveOverSelect=false); + + //! destructor + virtual ~CGUISTKListBox(); + + //! returns amount of list items + virtual u32 getItemCount() const; + + virtual const wchar_t* getCellText(u32 row_num, u32 col_num) const; + + virtual ListItem getItem(u32 id) const; + + //! clears the list + virtual void clear(); + + //! returns id of selected item. returns -1 if no item is selected. + virtual s32 getSelected() const; + + //! sets the selected item. Set this to -1 if no item should be selected + virtual void setSelected(s32 id); + + virtual s32 getRowByCellText(const wchar_t * text); + + //! sets the selected item. Set this to -1 if no item should be selected + virtual void setSelectedByCellText(const wchar_t * text); + + virtual s32 getRowByInternalName(const std::string & text) const; + + //! called if an event happened. + virtual bool OnEvent(const SEvent& event); + + //! draws the element and its children + virtual void draw(); + + //! adds an list item with an icon + //! \param text Text of list entry + //! \param icon Sprite index of the Icon within the current sprite bank. Set it to -1 if you want no icon + //! \return + //! returns the id of the new created item + //virtual u32 addItem(const wchar_t* text, s32 icon); + + virtual u32 addItem(const ListItem & item); + + //! Returns the icon of an item + virtual s32 getIcon(u32 row_num, u32 col_num) const; + + //! removes an item from the list + virtual void removeItem(u32 id); + + //! get the the id of the item at the given absolute coordinates + virtual s32 getItemAt(s32 xpos, s32 ypos) const; + + //! Sets the sprite bank which should be used to draw list icons. This font is set to the sprite bank of + //! the built-in-font by default. A sprite can be displayed in front of every list item. + //! An icon is an index within the icon sprite bank. Several default icons are available in the + //! skin through getIcon + virtual void setSpriteBank(IGUISpriteBank* bank); + + //! set whether the listbox should scroll to newly selected items + virtual void setAutoScrollEnabled(bool scroll); + + //! returns true if automatic scrolling is enabled, false if not. + virtual bool isAutoScrollEnabled() const; + + //! Update the position and size of the listbox, and update the scrollbar + virtual void updateAbsolutePosition(); + + //! set all item colors at given index to color + virtual void setItemOverrideColor(u32 index, video::SColor color); + + //! set all item colors of specified type at given index to color + virtual void setItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType, video::SColor color); + + //! clear all item colors at index + virtual void clearItemOverrideColor(u32 index); + + //! clear item color at index for given colortype + virtual void clearItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType); + + //! has the item at index its color overwritten? + virtual bool hasItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const; + + //! return the overwrite color at given item index. + virtual video::SColor getItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const; + + //! return the default color which is used for the given colorType + virtual video::SColor getItemDefaultColor(EGUI_LISTBOX_COLOR colorType) const; + + //! set the item at the given index + virtual void setCell(u32 row_num, u32 col_num, const wchar_t* text, s32 icon); + + //! Swap the items at the given indices + virtual void swapItems(u32 index1, u32 index2); + + //! set global itemHeight + virtual void setItemHeight( s32 height ); + + //! Sets whether to draw the background + virtual void setDrawBackground(bool draw); + + private: + + void recalculateItemHeight(); + void selectNew(s32 ypos, bool onlyHover=false); + void recalculateScrollPos(); + + // extracted that function to avoid copy&paste code + void recalculateIconWidth(); + + core::array< ListItem > Items; + s32 Selected; + s32 ItemHeight; + s32 ItemHeightOverride; + s32 TotalItemHeight; + s32 ItemsIconWidth; + gui::IGUIFont* Font; + gui::IGUISpriteBank* IconBank; + gui::IGUIScrollBar* ScrollBar; + u32 selectTime; + u32 LastKeyTime; + core::stringw KeyBuffer; + bool Selecting; + bool DrawBack; + bool MoveOverSelect; + bool AutoScroll; + bool HighlightWhenNotFocused; + }; + + + } // end namespace gui +} // end namespace irr + +#endif diff --git a/src/guiengine/widgets/bubble_widget.cpp b/src/guiengine/widgets/bubble_widget.cpp index a68343454..82fd4fbbb 100644 --- a/src/guiengine/widgets/bubble_widget.cpp +++ b/src/guiengine/widgets/bubble_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/bubble_widget.hpp b/src/guiengine/widgets/bubble_widget.hpp index f9fc20ad8..ee3f1e92f 100644 --- a/src/guiengine/widgets/bubble_widget.hpp +++ b/src/guiengine/widgets/bubble_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/button_widget.cpp b/src/guiengine/widgets/button_widget.cpp index 4ac93deea..feb18af83 100644 --- a/src/guiengine/widgets/button_widget.cpp +++ b/src/guiengine/widgets/button_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/button_widget.hpp b/src/guiengine/widgets/button_widget.hpp index 0dfa20690..98141bdc4 100644 --- a/src/guiengine/widgets/button_widget.hpp +++ b/src/guiengine/widgets/button_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/check_box_widget.cpp b/src/guiengine/widgets/check_box_widget.cpp index ada1ccd8c..35ee7308f 100644 --- a/src/guiengine/widgets/check_box_widget.cpp +++ b/src/guiengine/widgets/check_box_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/check_box_widget.hpp b/src/guiengine/widgets/check_box_widget.hpp index 806ce4825..e982fa113 100644 --- a/src/guiengine/widgets/check_box_widget.hpp +++ b/src/guiengine/widgets/check_box_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.cpp b/src/guiengine/widgets/dynamic_ribbon_widget.cpp index 0c8f7a4e2..0c408d757 100644 --- a/src/guiengine/widgets/dynamic_ribbon_widget.cpp +++ b/src/guiengine/widgets/dynamic_ribbon_widget.cpp @@ -1,5 +1,5 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Supertuxkart - a fun racing game with go-kart +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,6 +19,7 @@ #include "guiengine/widgets/dynamic_ribbon_widget.hpp" #include "io/file_manager.hpp" #include "states_screens/state_manager.hpp" +#include "utils/vs.hpp" #include #include @@ -29,10 +30,6 @@ using namespace GUIEngine; using namespace irr::core; using namespace irr::gui; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - DynamicRibbonWidget::DynamicRibbonWidget(const bool combo, const bool multi_row) : Widget(WTYPE_DYNAMIC_RIBBON) { m_scroll_offset = 0; @@ -268,8 +265,6 @@ void DynamicRibbonWidget::add() assert(m_row_amount != -1); } - // m_row_amount = (int)round((m_h - m_label_height) / (float)m_child_height); - if (m_properties[PROP_MAX_ROWS].size() > 0) { const int max_rows = atoi(m_properties[PROP_MAX_ROWS].c_str()); @@ -330,7 +325,7 @@ void DynamicRibbonWidget::buildInternalStructure() // ---- determine column amount const float row_height = (float)(m_h - m_label_height)/(float)m_row_amount; float ratio_zoom = (float)row_height / (float)(m_child_height - m_label_height); - m_col_amount = (int)round( m_w / ( m_child_width*ratio_zoom ) ); + m_col_amount = (int)roundf( m_w / ( m_child_width*ratio_zoom ) ); // ajust column amount to not add more item slots than we actually need const int item_count = m_items.size(); @@ -813,7 +808,7 @@ void DynamicRibbonWidget::propagateSelection() { if (ribbon != selected_ribbon) { - ribbon->m_selection[p] = (int)round(where*(ribbon->m_children.size()-1)); + ribbon->m_selection[p] = (int)roundf(where*(ribbon->m_children.size()-1)); ribbon->updateSelection(); } } @@ -945,37 +940,37 @@ void DynamicRibbonWidget::updateItemDisplay() IconButtonWidget* icon = dynamic_cast(&row.m_children[i]); assert(icon != NULL); - //FIXME : it is a bit hackish - if(i < item_placement[n].size()) - { - icon_id = item_placement[n][i]; - if (icon_id < item_amount && icon_id != -1) - { - std::string item_icon = (m_items[icon_id].m_animated ? - m_items[icon_id].m_all_images[0] : - m_items[icon_id].m_sshot_file); - icon->setImage( item_icon.c_str(), m_items[icon_id].m_image_path_type ); + //FIXME : it is a bit hackish + if(i < item_placement[n].size()) + { + icon_id = item_placement[n][i]; + if (icon_id < item_amount && icon_id != -1) + { + std::string item_icon = (m_items[icon_id].m_animated ? + m_items[icon_id].m_all_images[0] : + m_items[icon_id].m_sshot_file); + icon->setImage( item_icon.c_str(), m_items[icon_id].m_image_path_type ); - icon->m_properties[PROP_ID] = m_items[icon_id].m_code_name; - icon->setLabel(m_items[icon_id].m_user_name); - icon->m_text = m_items[icon_id].m_user_name; - icon->m_badges = m_items[icon_id].m_badges; + icon->m_properties[PROP_ID] = m_items[icon_id].m_code_name; + icon->setLabel(m_items[icon_id].m_user_name); + icon->m_text = m_items[icon_id].m_user_name; + icon->m_badges = m_items[icon_id].m_badges; - //std::cout << " item " << i << " is " << m_items[icon_id].m_code_name << "\n"; + //std::cout << " item " << i << " is " << m_items[icon_id].m_code_name << "\n"; - //std::wcout << L"Setting widget text '" << icon->m_text.c_str() << L"'\n"; + //std::wcout << L"Setting widget text '" << icon->m_text.c_str() << L"'\n"; - // if the ribbon has no "ribbon-wide" label, call will do nothing - row.setLabel(i, m_items[icon_id].m_user_name); - } - else - { - icon->setImage( "textures/transparence.png", IconButtonWidget::ICON_PATH_TYPE_RELATIVE ); - icon->resetAllBadges(); - icon->m_properties[PROP_ID] = RibbonWidget::NO_ITEM_ID; - //std::cout << " item " << i << " is a FILLER\n"; - } - } + // if the ribbon has no "ribbon-wide" label, call will do nothing + row.setLabel(i, m_items[icon_id].m_user_name); + } + else + { + icon->setImage( "textures/transparence.png", IconButtonWidget::ICON_PATH_TYPE_RELATIVE ); + icon->resetAllBadges(); + icon->m_properties[PROP_ID] = RibbonWidget::NO_ITEM_ID; + //std::cout << " item " << i << " is a FILLER\n"; + } + } } // next column } // next row } diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.hpp b/src/guiengine/widgets/dynamic_ribbon_widget.hpp index d55f96939..3b7796ef9 100644 --- a/src/guiengine/widgets/dynamic_ribbon_widget.hpp +++ b/src/guiengine/widgets/dynamic_ribbon_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/icon_button_widget.cpp b/src/guiengine/widgets/icon_button_widget.cpp index ef773790f..ad287b58e 100644 --- a/src/guiengine/widgets/icon_button_widget.cpp +++ b/src/guiengine/widgets/icon_button_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -72,8 +72,11 @@ void IconButtonWidget::add() Log::error("icon_button", "add() : error, cannot find texture '%s'.", m_properties[PROP_ICON].c_str()); - std::string file = file_manager->getDataDir() + "gui/main_help.png"; + std::string file = file_manager->getGUIDir() + "main_help.png"; m_texture = irr_driver->getTexture(file); + if(!m_texture) + Log::fatal("IconButtonWidget", + "Can't find fallback texture 'gui/main_help.png, aborting."); } m_texture_w = m_texture->getSize().Width; m_texture_h = m_texture->getSize().Height; diff --git a/src/guiengine/widgets/icon_button_widget.hpp b/src/guiengine/widgets/icon_button_widget.hpp index f400eb18d..745d45566 100644 --- a/src/guiengine/widgets/icon_button_widget.hpp +++ b/src/guiengine/widgets/icon_button_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/label_widget.cpp b/src/guiengine/widgets/label_widget.cpp index f55672e63..f7c202014 100644 --- a/src/guiengine/widgets/label_widget.cpp +++ b/src/guiengine/widgets/label_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -39,15 +39,17 @@ using namespace irr; LabelWidget::LabelWidget(bool title, bool bright) : Widget(WTYPE_LABEL) { m_title_font = title; - m_has_color = false; m_scroll_speed = 0; m_scroll_offset = 0; + m_bright = bright; - if (bright) + if (m_bright) { m_has_color = true; m_color = Skin::getColor("brighttext::neutral"); } + else + m_has_color = false; } // LabelWidget // ---------------------------------------------------------------------------- @@ -131,7 +133,6 @@ void LabelWidget::setText(const wchar_t *text, bool expandIfNeeded) if (expandIfNeeded) { assert(m_element != NULL); - const int fwidth = (m_title_font ? GUIEngine::getTitleFont() : GUIEngine::getFont())->getDimension(text).Width; core::rect rect = m_element->getRelativePosition(); @@ -173,7 +174,6 @@ bool LabelWidget::scrolledOff() const { // This method may only be called after this widget has been add()ed assert(m_element != NULL); - return m_scroll_offset <= -m_element->getAbsolutePosition().getWidth(); } @@ -185,3 +185,38 @@ void LabelWidget::setScrollSpeed(float speed) m_scroll_speed = speed; } // setScrollSpeed +// ---------------------------------------------------------------------------- + +void LabelWidget::setColor(const irr::video::SColor& color) +{ + assert(m_element != NULL); + m_color = color; + m_has_color = true; + ((IGUIStaticText*)m_element)->setOverrideColor(m_color); +} +// ---------------------------------------------------------------------------- + +void LabelWidget::setErrorColor() +{ + setColor(irr::video::SColor(255, 255, 0, 0)); +} + +// ---------------------------------------------------------------------------- + +void LabelWidget::setDefaultColor() +{ + if (m_bright) + { + setColor(Skin::getColor("brighttext::neutral")); + } + else + { + if(m_has_color) + { + assert(m_element != NULL); + m_has_color = false; + ((IGUIStaticText*)m_element)->enableOverrideColor(false); + } + } +} +// ---------------------------------------------------------------------------- diff --git a/src/guiengine/widgets/label_widget.hpp b/src/guiengine/widgets/label_widget.hpp index de227e550..674c4e641 100644 --- a/src/guiengine/widgets/label_widget.hpp +++ b/src/guiengine/widgets/label_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,6 +34,7 @@ namespace GUIEngine */ class LabelWidget : public Widget { + bool m_bright; bool m_has_color; irr::video::SColor m_color; @@ -58,14 +59,12 @@ namespace GUIEngine /** \brief Callback from base class Widget */ virtual void add(); - - /** Sets the color of the widget. + + /** Sets the color of the widget. * \param color The color to use for this widget. */ - void setColor(const irr::video::SColor& color) - { - m_color = color; - m_has_color = true; - } // setColor + void setColor(const irr::video::SColor& color); + void setErrorColor(); + void setDefaultColor(); /** \brief Callback from base class Widget */ virtual void update(float dt); @@ -87,11 +86,11 @@ namespace GUIEngine virtual void setText(const wchar_t *text, bool expandAsNeeded); /** Overloaded function which takes a stringw. */ - virtual void setText(const irr::core::stringw &s, bool expandAsNeeded) + virtual void setText(const irr::core::stringw &s, bool expandAsNeeded) { setText(s.c_str(), expandAsNeeded); } - + // -------------------------------------------------------------------- /** Sets horizontal scroll speed. */ diff --git a/src/guiengine/widgets/list_widget.cpp b/src/guiengine/widgets/list_widget.cpp index 04ef0d741..cbd35016f 100644 --- a/src/guiengine/widgets/list_widget.cpp +++ b/src/guiengine/widgets/list_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -15,14 +15,16 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include "guiengine/widgets/list_widget.hpp" + #include "guiengine/CGUISpriteBank.h" #include "guiengine/engine.hpp" -#include "guiengine/widgets/list_widget.hpp" #include "io/file_manager.hpp" #include +#include #include -#include +#include "IGUIFontBitmap.h" #include @@ -51,7 +53,7 @@ void ListWidget::setIcons(STKModifiedSpriteBank* icons, int size) if (m_use_icons) { - IGUIListBox* list = getIrrlichtElement(); + CGUISTKListBox* list = getIrrlichtElement(); assert(list != NULL); list->setSpriteBank(m_icons); @@ -81,8 +83,8 @@ void ListWidget::setIcons(STKModifiedSpriteBank* icons, int size) } -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- void ListWidget::add() { const int header_height = GUIEngine::getFontHeight() + 15; @@ -90,11 +92,32 @@ void ListWidget::add() rect widget_size = (m_header.size() > 0 ? rect(m_x, m_y + header_height, m_x + m_w, m_y + m_h) : rect(m_x, m_y, m_x + m_w, m_y + m_h) ); - IGUIListBox* list = GUIEngine::getGUIEnv()->addListBox (widget_size, m_parent, getNewID()); - list->setAutoScrollEnabled(false); + IGUISkin * current_skin = GUIEngine::getGUIEnv()->getSkin(); + IGUIFont * current_font = GUIEngine::getGUIEnv()->getBuiltInFont(); + CGUISTKListBox * list_box = new CGUISTKListBox( + GUIEngine::getGUIEnv(), + m_parent ? m_parent : GUIEngine::getGUIEnv()->getRootGUIElement(), + getNewID(), + widget_size, + true, + true, + false); - m_element = list; - m_element->setTabOrder( list->getID() ); + if (current_skin && current_skin->getSpriteBank()) + { + list_box->setSpriteBank(current_skin->getSpriteBank()); + } + else if (current_font && current_font->getType() == EGFT_BITMAP) + { + list_box->setSpriteBank( ((IGUIFontBitmap*)current_font)->getSpriteBank()); + } + + list_box->drop(); + + list_box->setAutoScrollEnabled(false); + + m_element = list_box; + m_element->setTabOrder( list_box->getID() ); if (m_header.size() > 0) { @@ -150,91 +173,129 @@ void ListWidget::clear() // May only be called AFTER this widget has been add()ed assert(m_element != NULL); - IGUIListBox* list = getIrrlichtElement(); + CGUISTKListBox* list = getIrrlichtElement(); assert(list != NULL); list->clear(); - m_items.clear(); } // ----------------------------------------------------------------------------- -void ListWidget::addItem(const std::string& internalName, - const irr::core::stringw& name, const int icon) +void ListWidget::addItem( const std::string& internal_name, + const irr::core::stringw &name, + const int icon, + bool center) +{ + // May only be called AFTER this widget has been add()ed + assert(m_element != NULL); + + ListCell cell(name, icon, 1, center); + ListItem newItem; + newItem.m_internal_name = internal_name; + newItem.m_contents.push_back(cell); + + CGUISTKListBox* list = getIrrlichtElement(); + assert(list != NULL); + + u32 itemID = list->addItem( newItem ); + if (m_use_icons) + { + list->setItemOverrideColor( itemID, gui::EGUI_LBC_ICON, video::SColor(255,255,255,255) ); + list->setItemOverrideColor( itemID, gui::EGUI_LBC_ICON_HIGHLIGHT, video::SColor(255,255,255,255) ); + } + newItem.m_current_id = itemID; +} + +// ----------------------------------------------------------------------------- + +void ListWidget::addItem(const std::string& internal_name, + PtrVector * contents) { // May only be called AFTER this widget has been add()ed assert(m_element != NULL); ListItem newItem; - newItem.m_label = name; - newItem.m_internal_name = internalName; + newItem.m_internal_name = internal_name; + for(int i = 0; i < (int)contents->size(); i++) + { + newItem.m_contents.push_back(*contents->get(i)); + } - IGUIListBox* list = getIrrlichtElement(); + CGUISTKListBox* list = getIrrlichtElement(); assert(list != NULL); - if (m_use_icons && icon != -1) + u32 itemID = list->addItem( newItem ); + if (m_use_icons) { - u32 itemID = list->addItem( name.c_str(), icon ); list->setItemOverrideColor( itemID, gui::EGUI_LBC_ICON, video::SColor(255,255,255,255) ); list->setItemOverrideColor( itemID, gui::EGUI_LBC_ICON_HIGHLIGHT, video::SColor(255,255,255,255) ); - newItem.m_current_id = itemID; } - else - { - newItem.m_current_id = list->addItem( name.c_str() ); - } - m_items.push_back(newItem); + newItem.m_current_id = itemID; } // ----------------------------------------------------------------------------- - -void ListWidget::renameItem(const int itemID, const irr::core::stringw newName, const int icon) +void ListWidget::renameCell(const int row_index, const int col_index, const irr::core::stringw newName, const int icon) { // May only be called AFTER this widget has been add()ed assert(m_element != NULL); - IGUIListBox* list = getIrrlichtElement(); + CGUISTKListBox* list = getIrrlichtElement(); assert(list != NULL); - m_items[itemID].m_label = newName; - list->setItem(itemID, newName.c_str(), icon); + list->setCell(row_index, col_index, newName.c_str(), icon); - list->setItemOverrideColor( itemID, EGUI_LBC_TEXT , video::SColor(255,0,0,0) ); - list->setItemOverrideColor( itemID, EGUI_LBC_TEXT_HIGHLIGHT, video::SColor(255,255,255,255) ); + list->setItemOverrideColor( row_index, EGUI_LBC_TEXT , video::SColor(255,0,0,0) ); + list->setItemOverrideColor( row_index, EGUI_LBC_TEXT_HIGHLIGHT, video::SColor(255,255,255,255) ); +} + +// ----------------------------------------------------------------------------- +void ListWidget::renameItem(const int row_index, const irr::core::stringw newName, const int icon) +{ + renameCell(row_index, 0, newName, icon); +} + +// ----------------------------------------------------------------------------- +void ListWidget::renameItem(const std::string & internal_name, const irr::core::stringw newName, const int icon) +{ + CGUISTKListBox* list = getIrrlichtElement(); + assert(list != NULL); + renameCell(list->getRowByInternalName(internal_name), 0, newName, icon); } // ----------------------------------------------------------------------------- std::string ListWidget::getSelectionInternalName() { - if (getSelectionID() == -1) return ""; - return m_items[ getSelectionID() ].m_internal_name; + CGUISTKListBox* list = getIrrlichtElement(); + assert(list != NULL); + if (getSelectionID() == -1 || (getSelectionID() >= (int)list->getItemCount())) + return ""; + return list->getItem(getSelectionID()).m_internal_name; } // ----------------------------------------------------------------------------- - -irr::core::stringw ListWidget::getSelectionLabel() const +irr::core::stringw ListWidget::getSelectionLabel(const int cell) const { - const IGUIListBox* list = getIrrlichtElement(); + const CGUISTKListBox* list = getIrrlichtElement(); assert(list != NULL); - return list->getListItem( list->getSelected() ); + return list->getCellText( list->getSelected(), cell); } // ----------------------------------------------------------------------------- void ListWidget::selectItemWithLabel(const irr::core::stringw& name) { - IGUIListBox* list = getIrrlichtElement(); + CGUISTKListBox* list = getIrrlichtElement(); assert(list != NULL); - return list->setSelected( name.c_str() ); + return list->setSelectedByCellText( name.c_str() ); } // ----------------------------------------------------------------------------- void ListWidget::unfocused(const int playerID, Widget* new_focus) { - IGUIListBox* list = getIrrlichtElement(); + CGUISTKListBox* list = getIrrlichtElement(); // remove selection when leaving list if (list != NULL) list->setSelected(-1); @@ -247,7 +308,7 @@ int ListWidget::getSelectionID() const // May only be called AFTER this widget has been add()ed assert(m_element != NULL); - return getIrrlichtElement()->getSelected(); + return getIrrlichtElement()->getSelected(); } // ----------------------------------------------------------------------------- @@ -257,7 +318,7 @@ void ListWidget::setSelectionID(const int index) // May only be called AFTER this widget has been add()ed assert(m_element != NULL); - IGUIListBox* irritem = getIrrlichtElement(); + CGUISTKListBox* irritem = getIrrlichtElement(); // auto-scroll to item when selecting something, don't auto-scroll when selecting nothing if (index != -1) @@ -280,8 +341,7 @@ int ListWidget::getItemCount() const // May only be called AFTER this widget has been add()ed assert(m_element != NULL); - const int count = getIrrlichtElement()->getItemCount(); - assert((int)m_items.size() == count); + const int count = getIrrlichtElement()->getItemCount(); return count; } @@ -291,7 +351,6 @@ int ListWidget::getItemCount() const void ListWidget::elementRemoved() { Widget::elementRemoved(); - m_items.clear(); for (int n=0; n(); + CGUISTKListBox* irritem = getIrrlichtElement(); if (red) { @@ -346,7 +391,7 @@ void ListWidget::markItemBlue(const int id, bool blue) // May only be called AFTER this widget has been add()ed assert(m_element != NULL); - IGUIListBox* irritem = getIrrlichtElement(); + CGUISTKListBox* irritem = getIrrlichtElement(); if (blue) { @@ -402,3 +447,11 @@ EventPropagation ListWidget::transmitEvent(Widget* w, return EVENT_LET; } + +// ----------------------------------------------------------------------------- +int ListWidget::getItemID(const std::string internalName) const +{ + const CGUISTKListBox* list = getIrrlichtElement(); + assert(list != NULL); + return list->getRowByInternalName(internalName); +} diff --git a/src/guiengine/widgets/list_widget.hpp b/src/guiengine/widgets/list_widget.hpp index 778e5578f..fd56316ae 100644 --- a/src/guiengine/widgets/list_widget.hpp +++ b/src/guiengine/widgets/list_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,10 +22,13 @@ #include +#include "guiengine/widgets/CGUISTKListBox.h" #include "guiengine/widget.hpp" #include "guiengine/widgets/button_widget.hpp" #include "utils/leak_check.hpp" #include "utils/ptr_vector.hpp" +#include "IGUIElement.h" + namespace irr { namespace gui { class STKModifiedSpriteBank; } } @@ -47,7 +50,6 @@ namespace GUIEngine class ListWidget : public Widget { friend class Skin; - /** \brief whether this list has icons */ bool m_use_icons; @@ -55,14 +57,6 @@ namespace GUIEngine /** \brief if m_use_icons is true, this will contain the icon bank */ irr::gui::STKModifiedSpriteBank* m_icons; - struct ListItem - { - std::string m_internal_name; - irr::core::stringw m_label; - int m_current_id; - }; - std::vector< ListItem > m_items; - PtrVector< ButtonWidget > m_header_elements; ButtonWidget* m_selected_column; @@ -92,8 +86,10 @@ namespace GUIEngine std::vector< Column > m_header; IListWidgetHeaderListener* m_listener; - + public: + typedef irr::gui::CGUISTKListBox::ListItem ListItem; + typedef ListItem::ListCell ListCell; LEAK_CHECK() @@ -130,8 +126,13 @@ namespace GUIEngine * \param icon ID of the icon within the icon bank. Only used if an icon bank was passed. * \pre may only be called after the widget has been added to the screen with add() */ - void addItem(const std::string& internal_name, - const irr::core::stringw &name, const int icon=-1); + void addItem( const std::string& internal_name, + const irr::core::stringw &name, + const int icon=-1, + bool center = false); + + void addItem( const std::string& internal_name, + PtrVector * contents); /** * \brief erases all items in the list @@ -157,7 +158,7 @@ namespace GUIEngine */ std::string getSelectionInternalName(); - irr::core::stringw getSelectionLabel() const; + irr::core::stringw getSelectionLabel(const int cell = 0) const; void selectItemWithLabel(const irr::core::stringw& name); @@ -177,18 +178,24 @@ namespace GUIEngine * \brief rename an item and/or change its icon based on its ID * \pre may only be called after the widget has been added to the screen with add() */ - void renameItem(const int itemID, const irr::core::stringw newName, const int icon=-1); + void renameCell(const int row_num, const int col_num, const irr::core::stringw newName, const int icon=-1); + /** + * renames first cell only + */ + void renameItem(const int row_num, const irr::core::stringw newName, const int icon=-1); + void renameItem(const std::string & internal_name, const irr::core::stringw newName, const int icon=-1); + /** * \brief rename an item and/or change its icon based on its internal name * \pre may only be called after the widget has been added to the screen with add() */ - void renameItem(const std::string internalName, const irr::core::stringw newName, + void renameCell(const std::string internalName, const int col_num, const irr::core::stringw newName, const int icon=-1) { const int id = getItemID(internalName); assert(id != -1); - renameItem( id, newName, icon ); + renameCell( id, col_num, newName, icon ); } /** diff --git a/src/guiengine/widgets/model_view_widget.cpp b/src/guiengine/widgets/model_view_widget.cpp index 045a7e957..569f8ca2a 100644 --- a/src/guiengine/widgets/model_view_widget.cpp +++ b/src/guiengine/widgets/model_view_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/model_view_widget.hpp b/src/guiengine/widgets/model_view_widget.hpp index e86934f60..b6c554cd7 100644 --- a/src/guiengine/widgets/model_view_widget.hpp +++ b/src/guiengine/widgets/model_view_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/progress_bar_widget.cpp b/src/guiengine/widgets/progress_bar_widget.cpp index 5aa0667aa..fc79403c5 100644 --- a/src/guiengine/widgets/progress_bar_widget.cpp +++ b/src/guiengine/widgets/progress_bar_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/progress_bar_widget.hpp b/src/guiengine/widgets/progress_bar_widget.hpp index db0c1cede..88dd7dd88 100644 --- a/src/guiengine/widgets/progress_bar_widget.hpp +++ b/src/guiengine/widgets/progress_bar_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/guiengine/widgets/rating_bar_widget.cpp b/src/guiengine/widgets/rating_bar_widget.cpp index 7ee7c1188..e6135d0f4 100644 --- a/src/guiengine/widgets/rating_bar_widget.cpp +++ b/src/guiengine/widgets/rating_bar_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,13 +16,16 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "guiengine/engine.hpp" +#include "guiengine/modaldialog.hpp" #include "guiengine/widgets/rating_bar_widget.hpp" #include "utils/string_utils.hpp" -#include +#include "utils/vs.hpp" #include #include #include +#include +#include using namespace GUIEngine; using namespace irr::core; @@ -31,16 +34,21 @@ using namespace irr; // ----------------------------------------------------------------------------- RatingBarWidget::RatingBarWidget() : Widget(WTYPE_RATINGBAR) { - m_rating = 0; - m_star_number = 0; + m_allow_voting = false; + m_rating = 0.0f; + m_hover_rating = 0.0f; + m_stars = 3; + m_steps = 3; + m_hovering = false; + for(int i = 0; i < m_stars; i++) + m_star_values.push_back(0); } // ----------------------------------------------------------------------------- void RatingBarWidget::add() { - rect widget_size = rect(m_x, m_y, m_x + m_w, m_y + m_h); + const irr::core::recti widget_size = rect(m_x, m_y, m_x + m_w, m_y + m_h); m_element = GUIEngine::getGUIEnv()->addButton(widget_size, m_parent, getNewNoFocusID(), NULL, L""); - m_id = m_element->getID(); m_element->setTabStop(false); m_element->setTabGroup(false); @@ -50,36 +58,65 @@ void RatingBarWidget::add() /** Get the current step of the star * * \param index The index of the star. - * \param max_step The number of different steps that a star can display. Two - * step are obligatory: full and empty. * \return The current step of the star. */ -int RatingBarWidget::getStepOfStar(int index, int max_step) +int RatingBarWidget::getStepsOfStar(int index) { - assert(index >= 0 && index < m_star_number); // Index must be between 0 and m_star_number - 1. - assert(max_step >= 2); // The maximun number of step must be superior or equals to 2. + assert(index >= 0 && index < m_stars); // Index must be between 0 and m_star_number - 1. - if (m_rating < index) - { - return 0; - } - else if (m_rating > index + 1) - { - return max_step - 1; - } - else - { - float step_size = 1 / (float)(max_step - 1); - - for (int i = 0; i < max_step; i++) - { - if (m_rating > index + step_size * (i - 0.5) - && m_rating < index + step_size * (i + 0.5)) - return i; - } - } - - return 0; - // TODO: Assert or throws a exception, what type? + return m_star_values[index]; } // getStepOfStar + + +void RatingBarWidget::setStepValues(float float_rating) +{ + for (int star = 0; star < m_stars; star++) + { + if (float_rating < star) + m_star_values[star] = 0; + else if (float_rating > star + 1) + m_star_values[star] = m_steps-1; + else + { + m_star_values[star] =(int)roundf((float_rating * (m_steps-1)) - (star*(m_steps-1))); + } + } +} + +// ----------------------------------------------------------------------------- + +void RatingBarWidget::setRating(float rating) +{ + m_rating = rating; + setStepValues(m_rating); +} + +// ----------------------------------------------------------------------------- + +void RatingBarWidget::setStepValuesByMouse(const core::position2di & mouse_position, const core::recti & stars_rect) +{ + if(m_allow_voting){ + if(stars_rect.isPointInside(mouse_position)) + { + m_hovering = true; + float exact_hover = (float)(mouse_position.X - stars_rect.UpperLeftCorner.X) / (float)stars_rect.getWidth() * (float)m_stars; + m_hover_rating = roundf(exact_hover * (m_steps-1)) / (m_steps-1); + setStepValues(m_hover_rating); + } + else if(m_hovering) + { + setStepValues(m_rating); + m_hovering = false; + } + } +} + +void RatingBarWidget::onClick() +{ + if(m_allow_voting) + m_rating = m_hover_rating; +} + + + diff --git a/src/guiengine/widgets/rating_bar_widget.hpp b/src/guiengine/widgets/rating_bar_widget.hpp index d0996d6cc..3f94493c7 100644 --- a/src/guiengine/widgets/rating_bar_widget.hpp +++ b/src/guiengine/widgets/rating_bar_widget.hpp @@ -1,5 +1,6 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon +// 2013 Glenn De Jonghe // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,10 +35,17 @@ namespace GUIEngine */ class RatingBarWidget : public Widget { - - float m_rating; - int m_star_number; - + private: + float m_rating; + float m_hover_rating; + int m_stars; + int m_steps; + std::vector m_star_values; + bool m_hovering; + bool m_allow_voting; + + void setStepValues(float rating); + public: LEAK_CHECK() @@ -45,21 +53,29 @@ namespace GUIEngine RatingBarWidget(); virtual ~RatingBarWidget() {} + + void add(); /** Change the rating value of the widget. */ - void setRating(float rating) { m_rating = rating; }; + void setRating(float rating); /** Get the current value of the widget. */ float getRating() {return m_rating; }; - /** Change the number of star of the widget. */ - void setStarNumber(int star_number) { m_star_number = star_number; }; + /** Change the number of stars of the widget. */ + void setStarNumber(int star_number) { m_stars = star_number; }; - /** Get the current number of star of the widget. */ - int getStarNumber() {return m_star_number; }; + /** Get the current number of stars of the widget. */ + int getStarNumber() {return m_stars; }; - int getStepOfStar(int index, int max_step); + int getStepsOfStar(int index); + + void setStepValuesByMouse(const core::position2di & mouse_position, const core::recti & stars_rect); + + virtual void onClick(); + + void allowVoting() { m_allow_voting = true; } }; } diff --git a/src/guiengine/widgets/ribbon_widget.cpp b/src/guiengine/widgets/ribbon_widget.cpp index 827576dc7..8d4ba9f6c 100644 --- a/src/guiengine/widgets/ribbon_widget.cpp +++ b/src/guiengine/widgets/ribbon_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,6 +28,7 @@ #include "io/file_manager.hpp" #include "states_screens/state_manager.hpp" #include "utils/string_utils.hpp" +#include "utils/vs.hpp" #include #include @@ -37,10 +38,6 @@ using namespace GUIEngine; using namespace irr::core; using namespace irr::gui; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - const char RibbonWidget::NO_ITEM_ID[] = "?"; @@ -64,6 +61,13 @@ RibbonWidget::RibbonWidget(const RibbonType type) : Widget(WTYPE_RIBBON) updateSelection(); } // RibbonWidget +// ---------------------------------------------------------------------------- + +RibbonWidget::~RibbonWidget() +{ + m_active_children.clearWithoutDeleting(); +} // ~RibbonWidget + // ---------------------------------------------------------------------------- void RibbonWidget::add() { @@ -84,7 +88,15 @@ void RibbonWidget::add() m_parent, id, L""); m_element = btn; - const int subbuttons_amount = m_children.size(); + m_active_children.clearWithoutDeleting(); // Is just a copy of m_children without the deactivated children. m_children takes care of memory. + for (int i=0; im_tab_stop = false; } - bool has_label_underneath = m_children[i].m_text.size() > 0; - if (m_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0) + bool has_label_underneath = m_active_children[i].m_text.size() > 0; + if (m_active_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0) { has_label_underneath = false; } @@ -126,7 +138,7 @@ void RibbonWidget::add() if (has_label_underneath) with_label++; else without_label++; - total_needed_space += m_children[i].m_w; + total_needed_space += m_active_children[i].m_w; } int free_w_space = m_w - total_needed_space; @@ -140,7 +152,7 @@ void RibbonWidget::add() //free_w_space = (int)(m_w - total_needed_space*global_zoom); const int one_button_space = - (int)round((float)m_w / (float)subbuttons_amount); + (int)roundf((float)m_w / (float)subbuttons_amount); int widget_x = -1; @@ -155,7 +167,7 @@ void RibbonWidget::add() / (with_label + without_label/2.0f)); const int small_tab = large_tab/2; - stringw& message = m_children[i].m_text; + stringw& message = m_active_children[i].m_text; if (message.size() == 0) @@ -179,7 +191,7 @@ void RibbonWidget::add() widget_x + small_tab/2-2, m_h); } - if (m_children[i].m_type == WTYPE_BUTTON) + if (m_active_children[i].m_type == WTYPE_BUTTON) { subbtn = GUIEngine::getGUIEnv() ->addButton(subsize, btn, getNewNoFocusID(), @@ -197,7 +209,7 @@ void RibbonWidget::add() subbtn->setOverrideFont(GUIEngine::getSmallFont()); } } - else if (m_children[i].m_type == WTYPE_ICON_BUTTON) + else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON) { rect icon_part = rect(15, 0, @@ -232,7 +244,7 @@ void RibbonWidget::add() same_id, L""); icon->setScaleImage(true); std::string filename = file_manager->getDataDir() - + m_children[i].m_properties[PROP_ICON]; + + m_active_children[i].m_properties[PROP_ICON]; icon->setImage( irr_driver->getTexture(filename.c_str()) ); icon->setUseAlphaChannel(true); icon->setDrawBorder(false); @@ -267,21 +279,21 @@ void RibbonWidget::add() fprintf(stderr, "Invalid tab bar contents\n"); } - m_children[i].m_element = subbtn; + m_active_children[i].m_element = subbtn; if (message.size() == 0) widget_x += small_tab/2; else widget_x += large_tab/2; } // ---- icon ribbons - else if (m_children[i].m_type == WTYPE_ICON_BUTTON) + else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON) { if (widget_x == -1) widget_x = one_button_space/2; // find how much space to keep for the label under the button. // consider font size, whether the label is multiline, etc... - bool has_label = m_children[i].m_text.size() > 0; + bool has_label = m_active_children[i].m_text.size() > 0; - if (m_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0) + if (m_active_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0) { has_label = false; } @@ -291,13 +303,22 @@ void RibbonWidget::add() : 10; float imageRatio = - (float)m_children[i].m_w / (float)m_children[i].m_h; + (float)m_active_children[i].m_w / (float)m_active_children[i].m_h; // calculate the size of the image std::string filename = file_manager->getDataDir() - + m_children[i].m_properties[PROP_ICON]; + + m_active_children[i].m_properties[PROP_ICON]; video::ITexture* image = irr_driver->getTexture((filename).c_str()); + if(!image) + { + std::string file = file_manager->getGUIDir() + "main_help.png"; + image = irr_driver->getTexture(file); + if(!image) + Log::fatal("RibbonWidget", + "Can't find fallback texture 'gui/main_help.png, aborting."); + } + float image_h = (float)image->getSize().Height; float image_w = image_h*imageRatio; @@ -320,17 +341,17 @@ void RibbonWidget::add() // ---- add bitmap button part // backup and restore position in case the same object is added // multiple times (FIXME: unclean) - int old_x = m_children[i].m_x; - int old_y = m_children[i].m_y; - int old_w = m_children[i].m_w; - int old_h = m_children[i].m_h; + int old_x = m_active_children[i].m_x; + int old_y = m_active_children[i].m_y; + int old_w = m_active_children[i].m_w; + int old_h = m_active_children[i].m_h; - m_children[i].m_x = widget_x - (int)(image_w*zoom/2.0f); - m_children[i].m_y = button_y; - m_children[i].m_w = (int)(image_w*zoom); - m_children[i].m_h = (int)(image_h*zoom); + m_active_children[i].m_x = widget_x - (int)(image_w*zoom/2.0f); + m_active_children[i].m_y = button_y; + m_active_children[i].m_w = (int)(image_w*zoom); + m_active_children[i].m_h = (int)(image_h*zoom); - IconButtonWidget* icon = ((IconButtonWidget*)m_children.get(i)); + IconButtonWidget* icon = ((IconButtonWidget*)m_active_children.get(i)); if (icon->m_properties[PROP_EXTEND_LABEL].size() == 0) { @@ -338,14 +359,14 @@ void RibbonWidget::add() StringUtils::toString(one_button_space - icon->m_w); } - m_children.get(i)->m_parent = btn; - m_children.get(i)->add(); + m_active_children.get(i)->m_parent = btn; + m_active_children.get(i)->add(); // restore backuped size and location (see above for more info) - m_children[i].m_x = old_x; - m_children[i].m_y = old_y; - m_children[i].m_w = old_w; - m_children[i].m_h = old_h; + m_active_children[i].m_x = old_x; + m_active_children[i].m_y = old_y; + m_active_children[i].m_w = old_w; + m_active_children[i].m_h = old_h; // the label itself will be added by the icon widget. since it // adds the label outside of the widget area it is assigned to, @@ -361,7 +382,7 @@ void RibbonWidget::add() //m_children[i].id = subbtn->getID(); - m_children[i].m_event_handler = this; + m_active_children[i].m_event_handler = this; }// next sub-button id = m_element->getID(); @@ -439,11 +460,11 @@ void RibbonWidget::removeChildNamed(const char* name) void RibbonWidget::select(std::string item, const int mousePlayerID) { - const int subbuttons_amount = m_children.size(); + const int subbuttons_amount = m_active_children.size(); for (int i=0; i= m_children.size()) + if (m_selection[playerID] >= m_active_children.size()) { if (m_listener != NULL) m_listener->onRibbonWidgetScroll(1); - m_selection[playerID] = m_event_handler ? m_children.size()-1 : 0; + m_selection[playerID] = m_event_handler ? m_active_children.size()-1 : 0; } updateSelection(); @@ -475,14 +496,14 @@ EventPropagation RibbonWidget::rightPressed(const int playerID) const int mousePlayerID = input_manager->getPlayerKeyboardID(); if (playerID == mousePlayerID || playerID == PLAYER_ID_GAME_MASTER) { - m_mouse_focus = m_children.get(m_selection[playerID]); + m_mouse_focus = m_active_children.get(m_selection[playerID]); } } // if we reached a filler item, move again (but don't wrap) if (getSelectionIDString(playerID) == RibbonWidget::NO_ITEM_ID) { - if (m_selection[playerID] + 1 < m_children.size()) + if (m_selection[playerID] + 1 < m_active_children.size()) { rightPressed(playerID); } @@ -496,7 +517,7 @@ EventPropagation RibbonWidget::leftPressed(const int playerID) { if (m_deactivated) return EVENT_LET; // empty ribbon, or only one item (can't move left) - if (m_children.size() < 2) return EVENT_LET; + if (m_active_children.size() < 2) return EVENT_LET; m_selection[playerID]--; if (m_selection[playerID] < 0) @@ -505,7 +526,7 @@ EventPropagation RibbonWidget::leftPressed(const int playerID) m_selection[playerID] = m_event_handler ? 0 - : m_children.size()-1; + : m_active_children.size()-1; } updateSelection(); @@ -515,7 +536,7 @@ EventPropagation RibbonWidget::leftPressed(const int playerID) const int mousePlayerID = input_manager->getPlayerKeyboardID(); if (playerID == mousePlayerID || playerID == PLAYER_ID_GAME_MASTER) { - m_mouse_focus = m_children.get(m_selection[playerID]); + m_mouse_focus = m_active_children.get(m_selection[playerID]); } } @@ -542,7 +563,7 @@ EventPropagation RibbonWidget::focused(const int playerID) { Widget::focused(playerID); - if (m_children.size() < 1) return EVENT_LET; // empty ribbon + if (m_active_children.size() < 1) return EVENT_LET; // empty ribbon if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS) { @@ -550,7 +571,7 @@ EventPropagation RibbonWidget::focused(const int playerID) if (m_mouse_focus == NULL && m_selection[playerID] != -1 && (playerID == mousePlayerID || playerID == PLAYER_ID_GAME_MASTER)) { - m_mouse_focus = m_children.get(m_selection[playerID]); + m_mouse_focus = m_active_children.get(m_selection[playerID]); m_mouse_focus->focused(playerID); } } @@ -558,7 +579,7 @@ EventPropagation RibbonWidget::focused(const int playerID) { if (m_selection[playerID] != -1) { - m_children.get(m_selection[playerID])->focused(playerID); + m_active_children.get(m_selection[playerID])->focused(playerID); } } @@ -572,11 +593,11 @@ EventPropagation RibbonWidget::focused(const int playerID) void RibbonWidget::unfocused(const int playerID, Widget* new_focus) { - if (new_focus != NULL && new_focus != this && !m_children.contains(new_focus)) + if (new_focus != NULL && new_focus != this && !m_active_children.contains(new_focus)) { - if (m_selection[playerID] != -1) + if (m_selection[playerID] >= 0 && m_selection[playerID] < m_children.size()) { - m_children.get(m_selection[playerID])->unfocused(playerID, new_focus); + m_active_children.get(m_selection[playerID])->unfocused(playerID, new_focus); } } @@ -589,7 +610,7 @@ EventPropagation RibbonWidget::mouseHovered(Widget* child, { if (m_deactivated) return EVENT_LET; - const int subbuttons_amount = m_children.size(); + const int subbuttons_amount = m_active_children.size(); if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS) { @@ -602,7 +623,7 @@ EventPropagation RibbonWidget::mouseHovered(Widget* child, { for (int i=0; i=m_children.size()) - return m_children[0].m_properties[PROP_ID]; - return m_children[m_selection[playerID]].m_properties[PROP_ID]; + if(m_selection[playerID]>=m_active_children.size()) + return m_active_children[0].m_properties[PROP_ID]; + return m_active_children[m_selection[playerID]].m_properties[PROP_ID]; } // getSelectionIDString // ---------------------------------------------------------------------------- void RibbonWidget::updateSelection() { - const int subbuttons_amount = m_children.size(); + const int subbuttons_amount = m_active_children.size(); // FIXME: m_selection, m_selected, m_mouse_focus... what a mess... @@ -648,12 +669,12 @@ void RibbonWidget::updateSelection() for (int i=0; ionDisabledItemClicked( - m_children[m_selection[playerID]].m_properties[PROP_ID]); + m_active_children[m_selection[playerID]].m_properties[PROP_ID]); return EVENT_BLOCK; } @@ -731,3 +752,12 @@ int RibbonWidget::findItemNamed(const char* internalName) } return -1; } // findItemNamed + +// ---------------------------------------------------------------------------- +Widget* RibbonWidget::findWidgetNamed(const char* internalName) +{ + int id = findItemNamed(internalName); + if (id >= 0) + return m_children.get(id); + return NULL; +} // findItemNamed diff --git a/src/guiengine/widgets/ribbon_widget.hpp b/src/guiengine/widgets/ribbon_widget.hpp index 1250874bc..954d6dd76 100644 --- a/src/guiengine/widgets/ribbon_widget.hpp +++ b/src/guiengine/widgets/ribbon_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -87,7 +87,7 @@ namespace GUIEngine PtrVector m_labels; IRibbonListener* m_listener; - + PtrVector m_active_children; public: @@ -106,7 +106,7 @@ namespace GUIEngine Widget* m_mouse_focus; RibbonWidget(const RibbonType type=RIBBON_COMBO); - virtual ~RibbonWidget() {} + virtual ~RibbonWidget(); void add(); @@ -155,6 +155,9 @@ namespace GUIEngine /** Returns the ID of the item, or -1 if not found */ int findItemNamed(const char* internalName); + /** Returns the the widget, or NULL if not found */ + GUIEngine::Widget * findWidgetNamed(const char* interalName); + /** \brief Dynamically (at runtime) add a text item to this ribbon * \pre This must be called before RibbonWidget::add, while the * widget is not yet displayed diff --git a/src/guiengine/widgets/spinner_widget.cpp b/src/guiengine/widgets/spinner_widget.cpp index c762f42b9..0b0c058a2 100644 --- a/src/guiengine/widgets/spinner_widget.cpp +++ b/src/guiengine/widgets/spinner_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -62,33 +62,17 @@ void SpinnerWidget::add() if (min_s.size() > 0) { - int i; - std::istringstream myStream(min_s); - myStream >> i; - bool is_number = (i!=0); - if (is_number) + if (!StringUtils::parseString(min_s, &m_min)) { - m_min = i; - } - else - { - std::cerr << "WARNING : invalid value for spinner widget minimum value : '" << min_s << "'\n"; + Log::warn("invalid value for spinner widget minimum value : %s", min_s.c_str()); } } if (max_s.size() > 0) { - int i; - std::istringstream myStream(max_s); - myStream >> i; - bool is_number = (i!=0); - if (is_number) - { - m_max = i; - } - else - { - std::cerr << "WARNING : invalid value for spinner widget maximal value : '" << max_s << "'\n"; + if (!StringUtils::parseString(max_s, &m_max)) + { + Log::warn("invalid value for spinner widget maximum value : %s", max_s.c_str()); } } diff --git a/src/guiengine/widgets/spinner_widget.hpp b/src/guiengine/widgets/spinner_widget.hpp index 51733127e..364dad936 100644 --- a/src/guiengine/widgets/spinner_widget.hpp +++ b/src/guiengine/widgets/spinner_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -161,6 +161,8 @@ namespace GUIEngine /** Override method from base class Widget */ virtual void setDeactivated(); + bool isActivated() { return !m_deactivated; } + /** Display custom text in spinner */ void setCustomText(const core::stringw& text); }; diff --git a/src/guiengine/widgets/text_box_widget.cpp b/src/guiengine/widgets/text_box_widget.cpp index e4bd5c5e0..494e73c38 100644 --- a/src/guiengine/widgets/text_box_widget.cpp +++ b/src/guiengine/widgets/text_box_widget.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -128,6 +128,15 @@ stringw TextBoxWidget::getText() const // ----------------------------------------------------------------------------- +void TextBoxWidget::setPasswordBox(bool passwordBox, wchar_t passwordChar) +{ + IGUIEditBox* textCtrl = Widget::getIrrlichtElement(); + assert(textCtrl != NULL); + textCtrl->setPasswordBox(passwordBox, passwordChar); +} + +// ----------------------------------------------------------------------------- + EventPropagation TextBoxWidget::focused(const int playerID) { assert(playerID == 0); // No support for multiple players in text areas! diff --git a/src/guiengine/widgets/text_box_widget.hpp b/src/guiengine/widgets/text_box_widget.hpp index 3c72a47f6..78426c772 100644 --- a/src/guiengine/widgets/text_box_widget.hpp +++ b/src/guiengine/widgets/text_box_widget.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -68,6 +68,7 @@ namespace GUIEngine void clearListeners(); irr::core::stringw getText() const; + void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*'); virtual void elementRemoved(); }; diff --git a/src/ide/Makefile.am b/src/ide/Makefile.am deleted file mode 100644 index 55e52e932..000000000 --- a/src/ide/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -# src/ide/ - -EXTRA_DIST = \ - $(shell find $(srcdir)/codeblocks -name "*.cbp") \ - $(shell find $(srcdir)/codeblocks -name "*.depend") \ - $(shell find $(srcdir)/codeblocks -name "*.layout") \ - codeblocks/README \ - codeblocks/supertuxkart.workspace \ - vc9/supertuxkart.sln \ - vc9/supertuxkart.vcproj \ - vc9/bullet_lib.vcproj \ - vc9/enet.vcproj \ - vc9/README \ - vc10/bullet_lib.vcxproj \ - vc10/enet.vcxproj \ - vc10/supertuxkart.sln \ - vc10/supertuxkart.vcxproj \ - vc10/supertuxkart.vcxproj.filters \ - Xcode/cleanupRelease.sh \ - Xcode/Config.xcconfig \ - Xcode/stk.icns \ - Xcode/SuperTuxKart-Info.plist \ - Xcode/STK_XCode.xcodeproj/project.pbxproj diff --git a/src/ide/Xcode/Config.xcconfig b/src/ide/Xcode/Config.xcconfig deleted file mode 100644 index f4df319c6..000000000 --- a/src/ide/Xcode/Config.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ -// Xcode build configuration - -HEADER_SEARCH_PATHS = /Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ "$(PROJECT_DIR)/../../" "$(PROJECT_DIR)/../../../lib/bullet/src" "$(PROJECT_DIR)/../../../lib/enet/include" /Library/Frameworks/fribidi.framework/Headers /Library/Frameworks/Ogg.framework/Headers /Library/Frameworks/Vorbis.framework/Headers /usr/local/include /usr/include - -OTHER_CFLAGS = -Wall -DHAVE_OGGVORBIS=1 -DHAS_SOCKLEN_T -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAS_POLL=1 -DHAS_FCNTL=1 -DHAS_INET_PTON=1 -DHAS_INET_NTOP=1 -DHAS_MSGHDR_FLAGS=1 -DENABLE_NLS=1 -DHAVE_GETTEXT=1 -DHAVE_GLUT=1 -DHAVE_IRRLICHT=1 -DPACKAGE="\"supertuxkart\"" -D__MACOSX__=1 -DHAVE_RTT=0 -DENABLE_BIDI=1 -fvisibility=hidden -DENABLE_WIIUSE=1 - -OTHER_LDFLAGS = -lcurl -LIBRARY_SEARCH_PATHS = /usr/lib /usr/local/lib diff --git a/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj b/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj deleted file mode 100644 index 65ac58400..000000000 --- a/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj +++ /dev/null @@ -1,3678 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 42; - objects = { - -/* Begin PBXBuildFile section */ - 95017B41124698C400C90D56 /* help_screen_4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95017B3F124698C400C90D56 /* help_screen_4.cpp */; }; - 9507E9B70FC1CCE900BD2B92 /* stk.icns in Resources */ = {isa = PBXBuildFile; fileRef = 9507E9B60FC1CCE900BD2B92 /* stk.icns */; }; - 9507E9D10FC1CDCE00BD2B92 /* Ogg.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = 9551C7F90FC1B63C00DB481B /* Ogg.framework */; }; - 9507E9D20FC1CDCE00BD2B92 /* OpenAL.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = 9551C7FA0FC1B63C00DB481B /* OpenAL.framework */; }; - 9507E9DB0FC1CDD500BD2B92 /* Vorbis.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = 9551C7FB0FC1B63C00DB481B /* Vorbis.framework */; }; - 950D448C118DEE3C006CFC41 /* CGUISpriteBank.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 950D448A118DEE3C006CFC41 /* CGUISpriteBank.cpp */; }; - 950D45D1118E040E006CFC41 /* options_screen_input2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 950D45CF118E040E006CFC41 /* options_screen_input2.cpp */; }; - 9516B07E12629C4E005F9493 /* sfx_buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9516B07C12629C4E005F9493 /* sfx_buffer.cpp */; }; - 951B50AE12C9698B004F6993 /* xml_writer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 951B50AD12C9698B004F6993 /* xml_writer.cpp */; }; - 951BC65E0FFAF290006B5FF1 /* ipo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 951BC65C0FFAF290006B5FF1 /* ipo.cpp */; }; - 9522F125107948AD0067ECF5 /* main_menu_screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F124107948AD0067ECF5 /* main_menu_screen.cpp */; }; - 9522F15B107949780067ECF5 /* race_setup_screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F15A107949780067ECF5 /* race_setup_screen.cpp */; }; - 9522F15E10794A350067ECF5 /* tracks_screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F15C10794A350067ECF5 /* tracks_screen.cpp */; }; - 9522F1E010795E8A0067ECF5 /* help_screen_1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F1DE10795E8A0067ECF5 /* help_screen_1.cpp */; }; - 9522F1E510795EFF0067ECF5 /* help_screen_2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F1E210795EFF0067ECF5 /* help_screen_2.cpp */; }; - 9522F1E610795EFF0067ECF5 /* help_screen_3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F1E410795EFF0067ECF5 /* help_screen_3.cpp */; }; - 9522F1F0107961560067ECF5 /* options_screen_input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F1EB107961560067ECF5 /* options_screen_input.cpp */; }; - 9522F1F1107961560067ECF5 /* options_screen_players.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9522F1ED107961560067ECF5 /* options_screen_players.cpp */; }; - 9524739610497C75000C197E /* dynamic_ribbon_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9524739510497C75000C197E /* dynamic_ribbon_widget.cpp */; }; - 95251F3D12554AB200505BA5 /* check_lap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95251F3B12554AB200505BA5 /* check_lap.cpp */; }; - 9525B71411C851D30094BD96 /* CBatchingMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9525B71211C851D30094BD96 /* CBatchingMesh.cpp */; }; - 95263DEC0FD7471900CF5F92 /* grand_prix_data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DE00FD7471900CF5F92 /* grand_prix_data.cpp */; }; - 95263DED0FD7471900CF5F92 /* grand_prix_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DE20FD7471900CF5F92 /* grand_prix_manager.cpp */; }; - 95263DEE0FD7471900CF5F92 /* highscore_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DE40FD7471900CF5F92 /* highscore_manager.cpp */; }; - 95263DEF0FD7471900CF5F92 /* highscores.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DE60FD7471900CF5F92 /* highscores.cpp */; }; - 95263DF00FD7471900CF5F92 /* history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DE80FD7471900CF5F92 /* history.cpp */; }; - 95263DF10FD7471900CF5F92 /* race_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DEA0FD7471900CF5F92 /* race_manager.cpp */; }; - 9527AA4513CFC345009188DB /* IrrFramework.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = 9538E2E812C2682B00172896 /* IrrFramework.framework */; }; - 9528C71612D69494006E9167 /* particle_kind_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9528C71412D69494006E9167 /* particle_kind_manager.cpp */; }; - 9528CC241291E7A10078A5EF /* binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9528CC221291E7A10078A5EF /* binding.cpp */; }; - 952997C715B205DE0028301A /* cutscene_world.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997BF15B205DE0028301A /* cutscene_world.cpp */; }; - 952997C815B205DE0028301A /* demo_world.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997C115B205DE0028301A /* demo_world.cpp */; }; - 952997C915B205DE0028301A /* game_tutorial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997C315B205DE0028301A /* game_tutorial.cpp */; }; - 952997ED15B206890028301A /* skidding_ai.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997EA15B206890028301A /* skidding_ai.cpp */; }; - 952997FB15B206D90028301A /* abstract_kart_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997F315B206D90028301A /* abstract_kart_animation.cpp */; }; - 952997FC15B206D90028301A /* cannon_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997F515B206D90028301A /* cannon_animation.cpp */; }; - 952997FD15B206D90028301A /* explosion_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997F715B206D90028301A /* explosion_animation.cpp */; }; - 952997FE15B206D90028301A /* rescue_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952997F915B206D90028301A /* rescue_animation.cpp */; }; - 9529980315B2070C0028301A /* cutscene_gui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9529980115B2070C0028301A /* cutscene_gui.cpp */; }; - 9529980715B2072E0028301A /* player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9529980615B2072E0028301A /* player.cpp */; }; - 9529980C15B207450028301A /* check_cannon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9529980A15B207450028301A /* check_cannon.cpp */; }; - 9529981115B207740028301A /* show_curve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9529980F15B207740028301A /* show_curve.cpp */; }; - 952A1545103F66D600B1895D /* camera.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A152D103F66D600B1895D /* camera.cpp */; }; - 952A1546103F66D600B1895D /* explosion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A152F103F66D600B1895D /* explosion.cpp */; }; - 952A1547103F66D600B1895D /* irr_driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A1531103F66D600B1895D /* irr_driver.cpp */; }; - 952A1548103F66D600B1895D /* material.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A1533103F66D600B1895D /* material.cpp */; }; - 952A1549103F66D600B1895D /* material_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A1535103F66D600B1895D /* material_manager.cpp */; }; - 952A154A103F66D600B1895D /* mesh_tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A1537103F66D600B1895D /* mesh_tools.cpp */; }; - 952A154B103F66D600B1895D /* moving_texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A1539103F66D600B1895D /* moving_texture.cpp */; }; - 952A154D103F66D600B1895D /* shadow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A153D103F66D600B1895D /* shadow.cpp */; }; - 952A154E103F66D600B1895D /* skid_marks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A153F103F66D600B1895D /* skid_marks.cpp */; }; - 952A1554103F68D000B1895D /* profile_world.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A1552103F68D000B1895D /* profile_world.cpp */; }; - 95376CAF1320784100C842A4 /* lod_node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95376CAD1320784100C842A4 /* lod_node.cpp */; }; - 953789730FC7829100DD1F8E /* graph_node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953789720FC7829100DD1F8E /* graph_node.cpp */; }; - 953789820FC7831400DD1F8E /* quad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953789810FC7831400DD1F8E /* quad.cpp */; }; - 9538A56012CD094200CE3220 /* addon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9538A55E12CD094200CE3220 /* addon.cpp */; }; - 9538E2B912C25D6800172896 /* addons_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9538E2B512C25D6800172896 /* addons_manager.cpp */; }; - 9538E2BA12C25D6800172896 /* network_http.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9538E2B712C25D6800172896 /* network_http.cpp */; }; - 9538E2E912C2682B00172896 /* IrrFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9538E2E812C2682B00172896 /* IrrFramework.framework */; }; - 95395A77129DFE130079BCE7 /* message_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95395A75129DFE130079BCE7 /* message_dialog.cpp */; }; - 953A959D13367D6E00D86B4D /* options_screen_ui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953A959B13367D6E00D86B4D /* options_screen_ui.cpp */; }; - 953F8B2111F7C13C00205E66 /* scalable_font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953F8B1F11F7C13C00205E66 /* scalable_font.cpp */; }; - 9542FC7712D3BDB000C00366 /* particle_emitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9542FC7512D3BDB000C00366 /* particle_emitter.cpp */; }; - 9542FD4C12D3E0D700C00366 /* particle_kind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9542FD4A12D3E0D700C00366 /* particle_kind.cpp */; }; - 9543D58F14D36EE3000B0888 /* kart_gfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9543D58D14D36EE3000B0888 /* kart_gfx.cpp */; }; - 9543D59E14D38831000B0888 /* select_challenge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9543D59D14D38831000B0888 /* select_challenge.cpp */; }; - 9545ABCA11E3E38300D3C37A /* progress_bar_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9545ABC811E3E38300D3C37A /* progress_bar_widget.cpp */; }; - 954E486A11B19C4100B1DF63 /* fribidi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 954E486911B19C4100B1DF63 /* fribidi.framework */; }; - 954E4C2D0FF98B6F0047FE3E /* animation_base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 954E4C250FF98B6E0047FE3E /* animation_base.cpp */; }; - 954E4C2F0FF98B6F0047FE3E /* billboard_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 954E4C290FF98B6E0047FE3E /* billboard_animation.cpp */; }; - 954E4C300FF98B6F0047FE3E /* three_d_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 954E4C2B0FF98B6E0047FE3E /* three_d_animation.cpp */; }; - 9551B27111DC0D4D002DD140 /* addons_screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9551B26D11DC0D4D002DD140 /* addons_screen.cpp */; }; - 9551B27C11DC0D6A002DD140 /* zip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9551B27811DC0D6A002DD140 /* zip.cpp */; }; - 9551C8270FC1B72500DB481B /* music_information.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2AC280F296540000D3E5D /* music_information.cpp */; }; - 9551C8280FC1B72500DB481B /* music_ogg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2AC2A0F296540000D3E5D /* music_ogg.cpp */; }; - 9551C8290FC1B72500DB481B /* sfx_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2AC2D0F296540000D3E5D /* sfx_manager.cpp */; }; - 9551C82A0FC1B72500DB481B /* sfx_openal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2AC2F0F296540000D3E5D /* sfx_openal.cpp */; }; - 9551C8900FC1B72500DB481B /* challenge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2AE260F296541000D3E5D /* challenge.cpp */; }; - 9551C8910FC1B72500DB481B /* challenge_data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2AE280F296541000D3E5D /* challenge_data.cpp */; }; - 9551C8920FC1B72500DB481B /* unlock_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2AE2A0F296541000D3E5D /* unlock_manager.cpp */; }; - 9551C8A20FC1B72500DB481B /* attachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B0F60F296545000D3E5D /* attachment.cpp */; }; - 9551C8A30FC1B72500DB481B /* attachment_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B0F80F296545000D3E5D /* attachment_manager.cpp */; }; - 9551C8A40FC1B72500DB481B /* bowling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B0FA0F296545000D3E5D /* bowling.cpp */; }; - 9551C8A60FC1B72500DB481B /* cake.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B0FE0F296545000D3E5D /* cake.cpp */; }; - 9551C8A70FC1B72500DB481B /* flyable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1000F296545000D3E5D /* flyable.cpp */; }; - 9551C8A80FC1B72500DB481B /* item.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1020F296545000D3E5D /* item.cpp */; }; - 9551C8A90FC1B72500DB481B /* item_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1040F296545000D3E5D /* item_manager.cpp */; }; - 9551C8AA0FC1B72500DB481B /* plunger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1060F296545000D3E5D /* plunger.cpp */; }; - 9551C8AB0FC1B72500DB481B /* powerup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1080F296545000D3E5D /* powerup.cpp */; }; - 9551C8AC0FC1B72500DB481B /* powerup_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B10A0F296545000D3E5D /* powerup_manager.cpp */; }; - 9551C8AD0FC1B72500DB481B /* projectile_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B10C0F296545000D3E5D /* projectile_manager.cpp */; }; - 9551C8AE0FC1B72500DB481B /* rubber_band.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B10E0F296545000D3E5D /* rubber_band.cpp */; }; - 9551C8AF0FC1B72500DB481B /* kart.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1180F296545000D3E5D /* kart.cpp */; }; - 9551C8B00FC1B72500DB481B /* kart_model.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B11B0F296545000D3E5D /* kart_model.cpp */; }; - 9551C8B10FC1B72500DB481B /* kart_properties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B11D0F296545000D3E5D /* kart_properties.cpp */; }; - 9551C8B20FC1B72500DB481B /* kart_properties_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B11F0F296545000D3E5D /* kart_properties_manager.cpp */; }; - 9551C8B30FC1B72500DB481B /* moveable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1210F296545000D3E5D /* moveable.cpp */; }; - 9551C8B90FC1B72500DB481B /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1360F296545000D3E5D /* main.cpp */; }; - 9551C8BA0FC1B72500DB481B /* main_loop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1380F296545000D3E5D /* main_loop.cpp */; }; - 9551C8BE0FC1B72500DB481B /* follow_the_leader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B14A0F296545000D3E5D /* follow_the_leader.cpp */; }; - 9551C8BF0FC1B72500DB481B /* linear_world.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B14C0F296545000D3E5D /* linear_world.cpp */; }; - 9551C8C00FC1B72500DB481B /* standard_race.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B14E0F296545000D3E5D /* standard_race.cpp */; }; - 9551C8C10FC1B72500DB481B /* three_strikes_battle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1500F296545000D3E5D /* three_strikes_battle.cpp */; }; - 9551C8C20FC1B72500DB481B /* world.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1520F296545000D3E5D /* world.cpp */; }; - 9551C8C30FC1B72500DB481B /* connect_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B15D0F296545000D3E5D /* connect_message.cpp */; }; - 9551C8C40FC1B72500DB481B /* kart_control_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1610F296545000D3E5D /* kart_control_message.cpp */; }; - 9551C8C50FC1B72500DB481B /* kart_update_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1630F296545000D3E5D /* kart_update_message.cpp */; }; - 9551C8C60FC1B72500DB481B /* message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1650F296545000D3E5D /* message.cpp */; }; - 9551C8C70FC1B72500DB481B /* network_kart.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1670F296545000D3E5D /* network_kart.cpp */; }; - 9551C8C80FC1B72500DB481B /* network_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1690F296545000D3E5D /* network_manager.cpp */; }; - 9551C8C90FC1B72500DB481B /* race_info_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B16C0F296545000D3E5D /* race_info_message.cpp */; }; - 9551C8CA0FC1B72500DB481B /* race_result_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B16F0F296545000D3E5D /* race_result_message.cpp */; }; - 9551C8CB0FC1B72500DB481B /* race_state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1720F296545000D3E5D /* race_state.cpp */; }; - 9551C8CC0FC1B72500DB481B /* btKart.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1800F296545000D3E5D /* btKart.cpp */; }; - 9551C8CD0FC1B72500DB481B /* btUprightConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1820F296545000D3E5D /* btUprightConstraint.cpp */; }; - 9551C8CE0FC1B72500DB481B /* physics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1870F296545000D3E5D /* physics.cpp */; }; - 9551C8D70FC1B72500DB481B /* track.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1CF0F296545000D3E5D /* track.cpp */; }; - 9551C8D80FC1B72500DB481B /* track_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1D10F296545000D3E5D /* track_manager.cpp */; }; - 9551C8DA0FC1B72500DB481B /* random_generator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1E20F296546000D3E5D /* random_generator.cpp */; }; - 9551C8DC0FC1B72500DB481B /* string_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1E60F296546000D3E5D /* string_utils.cpp */; }; - 9551C8DD0FC1B72500DB481B /* vec3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C2B1E80F296546000D3E5D /* vec3.cpp */; }; - 9551C8DE0FC1B72500DB481B /* terrain_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953EAAAE0F30A4220000D57D /* terrain_info.cpp */; }; - 9551C8DF0FC1B72500DB481B /* triangle_mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953EAAB10F30A4410000D57D /* triangle_mesh.cpp */; }; - 9551C8E00FC1B72500DB481B /* translation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953EAAB50F30A4650000D57D /* translation.cpp */; }; - 9551C8E10FC1B72500DB481B /* xml_node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C65D760F532F7D00BE7BA7 /* xml_node.cpp */; }; - 9551C8EC0FC1B72500DB481B /* file_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9505570E0F6963790056E88C /* file_manager.cpp */; }; - 9551C8F60FC1B72500DB481B /* input_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A1182A0F77EA3100B18B3D /* input_manager.cpp */; }; - 9551C8F70FC1B72500DB481B /* input_device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A1184A0F77FC3900B18B3D /* input_device.cpp */; }; - 9551C8F80FC1B72500DB481B /* device_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A1187A0F78024E00B18B3D /* device_manager.cpp */; }; - 9551C8F90FC1B72500DB481B /* physical_object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95CA59F70F82FCB7003323DB /* physical_object.cpp */; }; - 9551C8FA0FC1B72500DB481B /* quad_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 951C357E0FC05BF400A48379 /* quad_set.cpp */; }; - 9551C8FB0FC1B72500DB481B /* quad_graph.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 951C35800FC05BF400A48379 /* quad_graph.cpp */; }; - 9551C9F10FC1B7EE00DB481B /* Ogg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9551C7F90FC1B63C00DB481B /* Ogg.framework */; }; - 9551C9F20FC1B7EE00DB481B /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9551C7FA0FC1B63C00DB481B /* OpenAL.framework */; }; - 9551C9F30FC1B7EE00DB481B /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9551C7FB0FC1B63C00DB481B /* Vorbis.framework */; }; - 9551CBD10FC1BB7600DB481B /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950557640F6968BE0056E88C /* IOKit.framework */; }; - 9551CBD20FC1BB7600DB481B /* QuickTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9505575F0F6968A50056E88C /* QuickTime.framework */; }; - 9551CBD30FC1BB7600DB481B /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 950557570F6968860056E88C /* Carbon.framework */; }; - 9551CBD40FC1BB7600DB481B /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9505574E0F69684D0056E88C /* AudioUnit.framework */; }; - 9551CBD50FC1BB7600DB481B /* GLUT.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95F423400E26E65B00692113 /* GLUT.framework */; }; - 9551CBD60FC1BB7600DB481B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95F4231E0E26E44800692113 /* Cocoa.framework */; }; - 9551CBD70FC1BB7600DB481B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95F423120E26E3DC00692113 /* OpenGL.framework */; }; - 9551CBDA0FC1BB9200DB481B /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9551CBD90FC1BB9200DB481B /* AGL.framework */; }; - 9552C1FB1231249000347B6C /* world_with_rank.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9552C1F91231249000347B6C /* world_with_rank.cpp */; }; - 9553823A10FD4FEC00737979 /* constants.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9553823910FD4FEC00737979 /* constants.cpp */; }; - 9554C4A611F1188000906416 /* race_result_gui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9554C4A411F1188000906416 /* race_result_gui.cpp */; }; - 9556A880119EF976009C558F /* options_screen_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9556A87C119EF976009C558F /* options_screen_audio.cpp */; }; - 9556A881119EF976009C558F /* options_screen_video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9556A87E119EF976009C558F /* options_screen_video.cpp */; }; - 955764E612FB67EF005CE479 /* btKartRaycast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955764E412FB67EF005CE479 /* btKartRaycast.cpp */; }; - 95591C4412A5E91E00FB1E95 /* fribidi.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = 954E486911B19C4100B1DF63 /* fribidi.framework */; }; - 9559DE7F12FF777600350DE8 /* rain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9559DE7E12FF777600350DE8 /* rain.cpp */; }; - 955DE88310042701006A4F3C /* check_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955DE88110042701006A4F3C /* check_manager.cpp */; }; - 955DE88C1004273B006A4F3C /* check_structure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955DE8871004273B006A4F3C /* check_structure.cpp */; }; - 9560368C12187EFB00EB96C4 /* layout_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9560368A12187EFB00EB96C4 /* layout_manager.cpp */; }; - 956039BA1218C09E00EB96C4 /* abstract_top_level_container.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956039B81218C09E00EB96C4 /* abstract_top_level_container.cpp */; }; - 95634EF21126272C009C145D /* gp_info_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95634EF01126272C009C145D /* gp_info_dialog.cpp */; }; - 956541BB10DD5F0A00C83E99 /* arenas_screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956541B910DD5F0A00C83E99 /* arenas_screen.cpp */; }; - 956541E110DD628C00C83E99 /* add_device_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956541DF10DD628C00C83E99 /* add_device_dialog.cpp */; }; - 956830E01132EC9E00088D14 /* irr_debug_drawer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956830DE1132EC9E00088D14 /* irr_debug_drawer.cpp */; }; - 956B0A9F1232D2E900767CCD /* bubble_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956B0A9E1232D2E900767CCD /* bubble_widget.cpp */; }; - 956C6ED31128D3FB004336C8 /* controller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956C6EC81128D3FB004336C8 /* controller.cpp */; }; - 956C6ED51128D3FB004336C8 /* end_controller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956C6ECC1128D3FB004336C8 /* end_controller.cpp */; }; - 956C6ED71128D3FB004336C8 /* player_controller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956C6ED11128D3FB004336C8 /* player_controller.cpp */; }; - 956F557513527E4B005C291D /* race_gui_base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 956F557313527E4B005C291D /* race_gui_base.cpp */; }; - 9574F17C11206881008D202E /* world_status.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9574F17A11206881008D202E /* world_status.cpp */; }; - 95757211134F5B890037747B /* CGUIEditBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9575720F134F5B890037747B /* CGUIEditBox.cpp */; }; - 957817DC142142C500AD07B2 /* referee.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 957817DA142142C500AD07B2 /* referee.cpp */; }; - 957899B813C8E05F007AA5A3 /* profiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 957899B613C8E05F007AA5A3 /* profiler.cpp */; }; - 957957A214A3CA3900E72497 /* custom_video_settings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 957957A014A3CA3900E72497 /* custom_video_settings.cpp */; }; - 957A2D291405E21D00F45B22 /* hit_sfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 957A2D271405E21D00F45B22 /* hit_sfx.cpp */; }; - 957ED4801163FF18002AB42C /* ai_base_controller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 957ED47E1163FF18002AB42C /* ai_base_controller.cpp */; }; - 958330CC10122B4A00C5137E /* engine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330B210122B4A00C5137E /* engine.cpp */; }; - 958330CD10122B4A00C5137E /* event_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330B410122B4A00C5137E /* event_handler.cpp */; }; - 958330CE10122B4A00C5137E /* modaldialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330B610122B4A00C5137E /* modaldialog.cpp */; }; - 958330D010122B4A00C5137E /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330BA10122B4A00C5137E /* screen.cpp */; }; - 958330D110122B4A00C5137E /* screen_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330BC10122B4A00C5137E /* screen_loader.cpp */; }; - 958330D210122B4A00C5137E /* skin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330BD10122B4A00C5137E /* skin.cpp */; }; - 958330D310122B4A00C5137E /* widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330BF10122B4A00C5137E /* widget.cpp */; }; - 958330D410122B4A00C5137E /* credits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330C210122B4A00C5137E /* credits.cpp */; }; - 958330D510122B4A00C5137E /* kart_selection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330C410122B4A00C5137E /* kart_selection.cpp */; }; - 958330D710122B4A00C5137E /* race_gui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330C810122B4A00C5137E /* race_gui.cpp */; }; - 958330D810122B4A00C5137E /* state_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958330CA10122B4A00C5137E /* state_manager.cpp */; }; - 9583319910123B0200C5137E /* abstract_state_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9583319810123B0200C5137E /* abstract_state_manager.cpp */; }; - 9583323F101243ED00C5137E /* enter_player_name_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95833237101243ED00C5137E /* enter_player_name_dialog.cpp */; }; - 95833240101243ED00C5137E /* player_info_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95833239101243ED00C5137E /* player_info_dialog.cpp */; }; - 95833241101243ED00C5137E /* press_a_key_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9583323B101243ED00C5137E /* press_a_key_dialog.cpp */; }; - 95833242101243ED00C5137E /* track_info_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9583323D101243ED00C5137E /* track_info_dialog.cpp */; }; - 9584449E1330F89100CEA60A /* dictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9584448A1330F89100CEA60A /* dictionary.cpp */; }; - 9584449F1330F89100CEA60A /* dictionary_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9584448C1330F89100CEA60A /* dictionary_manager.cpp */; }; - 958444A01330F89100CEA60A /* iconv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9584448F1330F89100CEA60A /* iconv.cpp */; }; - 958444A11330F89100CEA60A /* language.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958444911330F89100CEA60A /* language.cpp */; }; - 958444A31330F89100CEA60A /* plural_forms.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958444961330F89100CEA60A /* plural_forms.cpp */; }; - 958444A41330F89100CEA60A /* po_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958444981330F89100CEA60A /* po_parser.cpp */; }; - 958444A51330F89100CEA60A /* stk_file_system.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9584449A1330F89100CEA60A /* stk_file_system.cpp */; }; - 958444A61330F89100CEA60A /* tinygettext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9584449C1330F89100CEA60A /* tinygettext.cpp */; }; - 9584B30D137CAC12008565D7 /* news_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9584B309137CAC12008565D7 /* news_manager.cpp */; }; - 9584B30E137CAC12008565D7 /* request.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9584B30B137CAC12008565D7 /* request.cpp */; }; - 9586318411B1EC9F00B8B4AF /* grand_prix_lose.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9586318011B1EC9F00B8B4AF /* grand_prix_lose.cpp */; }; - 9586318511B1EC9F00B8B4AF /* grand_prix_win.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9586318211B1EC9F00B8B4AF /* grand_prix_win.cpp */; }; - 958806E913EB675F005F90FE /* track_sector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958806E713EB675F005F90FE /* track_sector.cpp */; }; - 958949A9163F52FB00957345 /* inetwork_http.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958949A8163F52FB00957345 /* inetwork_http.cpp */; }; - 958BD770117F6AE90095B483 /* music_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958BD76E117F6AE90095B483 /* music_manager.cpp */; }; - 958D8C54104F523000A81934 /* race_paused_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958D8C53104F523000A81934 /* race_paused_dialog.cpp */; }; - 958D924213DA48A70097BC82 /* post_processing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958D924013DA48A70097BC82 /* post_processing.cpp */; }; - 9592DC6D13021B350039DBC8 /* minimal_race_gui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9592DC6B13021B350039DBC8 /* minimal_race_gui.cpp */; }; - 9593366B149EC9B10031FD41 /* overworld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95933669149EC9B10031FD41 /* overworld.cpp */; }; - 9593A2551609386100AB5EEB /* btAxisSweep3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A11C1609386100AB5EEB /* btAxisSweep3.cpp */; }; - 9593A2561609386100AB5EEB /* btBroadphaseProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A11F1609386100AB5EEB /* btBroadphaseProxy.cpp */; }; - 9593A2571609386100AB5EEB /* btCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1211609386100AB5EEB /* btCollisionAlgorithm.cpp */; }; - 9593A2581609386100AB5EEB /* btDbvt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1231609386100AB5EEB /* btDbvt.cpp */; }; - 9593A2591609386100AB5EEB /* btDbvtBroadphase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1251609386100AB5EEB /* btDbvtBroadphase.cpp */; }; - 9593A25A1609386100AB5EEB /* btDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1271609386100AB5EEB /* btDispatcher.cpp */; }; - 9593A25B1609386100AB5EEB /* btMultiSapBroadphase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1291609386100AB5EEB /* btMultiSapBroadphase.cpp */; }; - 9593A25C1609386100AB5EEB /* btOverlappingPairCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A12B1609386100AB5EEB /* btOverlappingPairCache.cpp */; }; - 9593A25D1609386100AB5EEB /* btQuantizedBvh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A12E1609386100AB5EEB /* btQuantizedBvh.cpp */; }; - 9593A25E1609386100AB5EEB /* btSimpleBroadphase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1301609386100AB5EEB /* btSimpleBroadphase.cpp */; }; - 9593A25F1609386100AB5EEB /* btActivatingCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1331609386100AB5EEB /* btActivatingCollisionAlgorithm.cpp */; }; - 9593A2601609386100AB5EEB /* btBox2dBox2dCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1351609386100AB5EEB /* btBox2dBox2dCollisionAlgorithm.cpp */; }; - 9593A2611609386100AB5EEB /* btBoxBoxCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1371609386100AB5EEB /* btBoxBoxCollisionAlgorithm.cpp */; }; - 9593A2621609386100AB5EEB /* btBoxBoxDetector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1391609386100AB5EEB /* btBoxBoxDetector.cpp */; }; - 9593A2631609386100AB5EEB /* btCollisionDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A13D1609386100AB5EEB /* btCollisionDispatcher.cpp */; }; - 9593A2641609386100AB5EEB /* btCollisionObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A13F1609386100AB5EEB /* btCollisionObject.cpp */; }; - 9593A2651609386100AB5EEB /* btCollisionWorld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1411609386100AB5EEB /* btCollisionWorld.cpp */; }; - 9593A2661609386100AB5EEB /* btCompoundCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1431609386100AB5EEB /* btCompoundCollisionAlgorithm.cpp */; }; - 9593A2671609386100AB5EEB /* btConvex2dConvex2dAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1451609386100AB5EEB /* btConvex2dConvex2dAlgorithm.cpp */; }; - 9593A2681609386100AB5EEB /* btConvexConcaveCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1471609386100AB5EEB /* btConvexConcaveCollisionAlgorithm.cpp */; }; - 9593A2691609386100AB5EEB /* btConvexConvexAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1491609386100AB5EEB /* btConvexConvexAlgorithm.cpp */; }; - 9593A26A1609386100AB5EEB /* btConvexPlaneCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A14B1609386100AB5EEB /* btConvexPlaneCollisionAlgorithm.cpp */; }; - 9593A26B1609386100AB5EEB /* btDefaultCollisionConfiguration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A14D1609386100AB5EEB /* btDefaultCollisionConfiguration.cpp */; }; - 9593A26C1609386100AB5EEB /* btEmptyCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A14F1609386100AB5EEB /* btEmptyCollisionAlgorithm.cpp */; }; - 9593A26D1609386100AB5EEB /* btGhostObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1511609386100AB5EEB /* btGhostObject.cpp */; }; - 9593A26E1609386100AB5EEB /* btInternalEdgeUtility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1531609386100AB5EEB /* btInternalEdgeUtility.cpp */; }; - 9593A26F1609386100AB5EEB /* btManifoldResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1551609386100AB5EEB /* btManifoldResult.cpp */; }; - 9593A2701609386100AB5EEB /* btSimulationIslandManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1571609386100AB5EEB /* btSimulationIslandManager.cpp */; }; - 9593A2711609386100AB5EEB /* btSphereBoxCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1591609386100AB5EEB /* btSphereBoxCollisionAlgorithm.cpp */; }; - 9593A2721609386100AB5EEB /* btSphereSphereCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A15B1609386100AB5EEB /* btSphereSphereCollisionAlgorithm.cpp */; }; - 9593A2731609386100AB5EEB /* btSphereTriangleCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A15D1609386100AB5EEB /* btSphereTriangleCollisionAlgorithm.cpp */; }; - 9593A2741609386100AB5EEB /* btUnionFind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A15F1609386100AB5EEB /* btUnionFind.cpp */; }; - 9593A2751609386100AB5EEB /* SphereTriangleDetector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1611609386100AB5EEB /* SphereTriangleDetector.cpp */; }; - 9593A2761609386100AB5EEB /* btBox2dShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1641609386100AB5EEB /* btBox2dShape.cpp */; }; - 9593A2771609386100AB5EEB /* btBoxShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1661609386100AB5EEB /* btBoxShape.cpp */; }; - 9593A2781609386100AB5EEB /* btBvhTriangleMeshShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1681609386100AB5EEB /* btBvhTriangleMeshShape.cpp */; }; - 9593A2791609386100AB5EEB /* btCapsuleShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A16A1609386100AB5EEB /* btCapsuleShape.cpp */; }; - 9593A27A1609386100AB5EEB /* btCollisionShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A16D1609386100AB5EEB /* btCollisionShape.cpp */; }; - 9593A27B1609386100AB5EEB /* btCompoundShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A16F1609386100AB5EEB /* btCompoundShape.cpp */; }; - 9593A27C1609386100AB5EEB /* btConcaveShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1711609386100AB5EEB /* btConcaveShape.cpp */; }; - 9593A27D1609386100AB5EEB /* btConeShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1731609386100AB5EEB /* btConeShape.cpp */; }; - 9593A27E1609386100AB5EEB /* btConvex2dShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1751609386100AB5EEB /* btConvex2dShape.cpp */; }; - 9593A27F1609386100AB5EEB /* btConvexHullShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1771609386100AB5EEB /* btConvexHullShape.cpp */; }; - 9593A2801609386100AB5EEB /* btConvexInternalShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1791609386100AB5EEB /* btConvexInternalShape.cpp */; }; - 9593A2811609386100AB5EEB /* btConvexPointCloudShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A17B1609386100AB5EEB /* btConvexPointCloudShape.cpp */; }; - 9593A2821609386100AB5EEB /* btConvexPolyhedron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A17D1609386100AB5EEB /* btConvexPolyhedron.cpp */; }; - 9593A2831609386100AB5EEB /* btConvexShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A17F1609386100AB5EEB /* btConvexShape.cpp */; }; - 9593A2841609386100AB5EEB /* btConvexTriangleMeshShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1811609386100AB5EEB /* btConvexTriangleMeshShape.cpp */; }; - 9593A2851609386100AB5EEB /* btCylinderShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1831609386100AB5EEB /* btCylinderShape.cpp */; }; - 9593A2861609386100AB5EEB /* btEmptyShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1851609386100AB5EEB /* btEmptyShape.cpp */; }; - 9593A2871609386100AB5EEB /* btHeightfieldTerrainShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1871609386100AB5EEB /* btHeightfieldTerrainShape.cpp */; }; - 9593A2881609386100AB5EEB /* btMinkowskiSumShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A18A1609386100AB5EEB /* btMinkowskiSumShape.cpp */; }; - 9593A2891609386100AB5EEB /* btMultimaterialTriangleMeshShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A18C1609386100AB5EEB /* btMultimaterialTriangleMeshShape.cpp */; }; - 9593A28A1609386100AB5EEB /* btMultiSphereShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A18E1609386100AB5EEB /* btMultiSphereShape.cpp */; }; - 9593A28B1609386100AB5EEB /* btOptimizedBvh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1901609386100AB5EEB /* btOptimizedBvh.cpp */; }; - 9593A28C1609386100AB5EEB /* btPolyhedralConvexShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1921609386100AB5EEB /* btPolyhedralConvexShape.cpp */; }; - 9593A28D1609386100AB5EEB /* btScaledBvhTriangleMeshShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1941609386100AB5EEB /* btScaledBvhTriangleMeshShape.cpp */; }; - 9593A28E1609386100AB5EEB /* btShapeHull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1961609386100AB5EEB /* btShapeHull.cpp */; }; - 9593A28F1609386100AB5EEB /* btSphereShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1981609386100AB5EEB /* btSphereShape.cpp */; }; - 9593A2901609386100AB5EEB /* btStaticPlaneShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A19A1609386100AB5EEB /* btStaticPlaneShape.cpp */; }; - 9593A2911609386100AB5EEB /* btStridingMeshInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A19C1609386100AB5EEB /* btStridingMeshInterface.cpp */; }; - 9593A2921609386100AB5EEB /* btTetrahedronShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A19E1609386100AB5EEB /* btTetrahedronShape.cpp */; }; - 9593A2931609386100AB5EEB /* btTriangleBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1A01609386100AB5EEB /* btTriangleBuffer.cpp */; }; - 9593A2941609386100AB5EEB /* btTriangleCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1A21609386100AB5EEB /* btTriangleCallback.cpp */; }; - 9593A2951609386100AB5EEB /* btTriangleIndexVertexArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1A41609386100AB5EEB /* btTriangleIndexVertexArray.cpp */; }; - 9593A2961609386100AB5EEB /* btTriangleIndexVertexMaterialArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1A61609386100AB5EEB /* btTriangleIndexVertexMaterialArray.cpp */; }; - 9593A2971609386100AB5EEB /* btTriangleMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1A91609386100AB5EEB /* btTriangleMesh.cpp */; }; - 9593A2981609386100AB5EEB /* btTriangleMeshShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1AB1609386100AB5EEB /* btTriangleMeshShape.cpp */; }; - 9593A2991609386100AB5EEB /* btUniformScalingShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1AE1609386100AB5EEB /* btUniformScalingShape.cpp */; }; - 9593A29A1609386100AB5EEB /* btContactProcessing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1B31609386100AB5EEB /* btContactProcessing.cpp */; }; - 9593A29B1609386100AB5EEB /* btGenericPoolAllocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1B51609386100AB5EEB /* btGenericPoolAllocator.cpp */; }; - 9593A29C1609386100AB5EEB /* btGImpactBvh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1B81609386100AB5EEB /* btGImpactBvh.cpp */; }; - 9593A29D1609386100AB5EEB /* btGImpactCollisionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1BA1609386100AB5EEB /* btGImpactCollisionAlgorithm.cpp */; }; - 9593A29E1609386100AB5EEB /* btGImpactQuantizedBvh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1BD1609386100AB5EEB /* btGImpactQuantizedBvh.cpp */; }; - 9593A29F1609386100AB5EEB /* btGImpactShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1BF1609386100AB5EEB /* btGImpactShape.cpp */; }; - 9593A2A01609386100AB5EEB /* btTriangleShapeEx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1C21609386100AB5EEB /* btTriangleShapeEx.cpp */; }; - 9593A2A11609386100AB5EEB /* gim_box_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1C81609386100AB5EEB /* gim_box_set.cpp */; }; - 9593A2A21609386100AB5EEB /* gim_contact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1CB1609386100AB5EEB /* gim_contact.cpp */; }; - 9593A2A31609386100AB5EEB /* gim_memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1D21609386100AB5EEB /* gim_memory.cpp */; }; - 9593A2A41609386100AB5EEB /* gim_tri_collision.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1D51609386100AB5EEB /* gim_tri_collision.cpp */; }; - 9593A2A51609386100AB5EEB /* btContinuousConvexCollision.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1D81609386100AB5EEB /* btContinuousConvexCollision.cpp */; }; - 9593A2A61609386100AB5EEB /* btConvexCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1DA1609386100AB5EEB /* btConvexCast.cpp */; }; - 9593A2A71609386100AB5EEB /* btGjkConvexCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1DE1609386100AB5EEB /* btGjkConvexCast.cpp */; }; - 9593A2A81609386100AB5EEB /* btGjkEpa2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1E01609386100AB5EEB /* btGjkEpa2.cpp */; }; - 9593A2A91609386100AB5EEB /* btGjkEpaPenetrationDepthSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1E21609386100AB5EEB /* btGjkEpaPenetrationDepthSolver.cpp */; }; - 9593A2AA1609386100AB5EEB /* btGjkPairDetector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1E41609386100AB5EEB /* btGjkPairDetector.cpp */; }; - 9593A2AB1609386100AB5EEB /* btMinkowskiPenetrationDepthSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1E71609386100AB5EEB /* btMinkowskiPenetrationDepthSolver.cpp */; }; - 9593A2AC1609386100AB5EEB /* btPersistentManifold.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1E91609386100AB5EEB /* btPersistentManifold.cpp */; }; - 9593A2AD1609386100AB5EEB /* btPolyhedralContactClipping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1EC1609386100AB5EEB /* btPolyhedralContactClipping.cpp */; }; - 9593A2AE1609386100AB5EEB /* btRaycastCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1EE1609386100AB5EEB /* btRaycastCallback.cpp */; }; - 9593A2AF1609386100AB5EEB /* btSubSimplexConvexCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1F11609386100AB5EEB /* btSubSimplexConvexCast.cpp */; }; - 9593A2B01609386100AB5EEB /* btVoronoiSimplexSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1F31609386100AB5EEB /* btVoronoiSimplexSolver.cpp */; }; - 9593A2B11609386100AB5EEB /* btKinematicCharacterController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1F81609386100AB5EEB /* btKinematicCharacterController.cpp */; }; - 9593A2B21609386100AB5EEB /* btConeTwistConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1FB1609386100AB5EEB /* btConeTwistConstraint.cpp */; }; - 9593A2B31609386100AB5EEB /* btContactConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A1FE1609386100AB5EEB /* btContactConstraint.cpp */; }; - 9593A2B41609386100AB5EEB /* btGeneric6DofConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2011609386100AB5EEB /* btGeneric6DofConstraint.cpp */; }; - 9593A2B51609386100AB5EEB /* btGeneric6DofSpringConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2031609386100AB5EEB /* btGeneric6DofSpringConstraint.cpp */; }; - 9593A2B61609386100AB5EEB /* btHinge2Constraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2051609386100AB5EEB /* btHinge2Constraint.cpp */; }; - 9593A2B71609386100AB5EEB /* btHingeConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2071609386100AB5EEB /* btHingeConstraint.cpp */; }; - 9593A2B81609386100AB5EEB /* btPoint2PointConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A20A1609386100AB5EEB /* btPoint2PointConstraint.cpp */; }; - 9593A2B91609386100AB5EEB /* btSequentialImpulseConstraintSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A20C1609386100AB5EEB /* btSequentialImpulseConstraintSolver.cpp */; }; - 9593A2BA1609386100AB5EEB /* btSliderConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A20E1609386100AB5EEB /* btSliderConstraint.cpp */; }; - 9593A2BB1609386100AB5EEB /* btSolve2LinearConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2101609386100AB5EEB /* btSolve2LinearConstraint.cpp */; }; - 9593A2BC1609386100AB5EEB /* btTypedConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2141609386100AB5EEB /* btTypedConstraint.cpp */; }; - 9593A2BD1609386100AB5EEB /* btUniversalConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2161609386100AB5EEB /* btUniversalConstraint.cpp */; }; - 9593A2BE1609386100AB5EEB /* btContinuousDynamicsWorld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A21A1609386100AB5EEB /* btContinuousDynamicsWorld.cpp */; }; - 9593A2BF1609386100AB5EEB /* btDiscreteDynamicsWorld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A21C1609386100AB5EEB /* btDiscreteDynamicsWorld.cpp */; }; - 9593A2C01609386100AB5EEB /* btRigidBody.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A21F1609386100AB5EEB /* btRigidBody.cpp */; }; - 9593A2C11609386100AB5EEB /* btSimpleDynamicsWorld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2211609386100AB5EEB /* btSimpleDynamicsWorld.cpp */; }; - 9593A2C21609386100AB5EEB /* Bullet-C-API.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2231609386100AB5EEB /* Bullet-C-API.cpp */; }; - 9593A2C31609386100AB5EEB /* btRaycastVehicle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2251609386100AB5EEB /* btRaycastVehicle.cpp */; }; - 9593A2C41609386100AB5EEB /* btWheelInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2281609386100AB5EEB /* btWheelInfo.cpp */; }; - 9593A2C51609386100AB5EEB /* btAlignedAllocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A22C1609386100AB5EEB /* btAlignedAllocator.cpp */; }; - 9593A2C61609386100AB5EEB /* btConvexHull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A22F1609386100AB5EEB /* btConvexHull.cpp */; }; - 9593A2C71609386100AB5EEB /* btConvexHullComputer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2311609386100AB5EEB /* btConvexHullComputer.cpp */; }; - 9593A2C81609386100AB5EEB /* btGeometryUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2341609386100AB5EEB /* btGeometryUtil.cpp */; }; - 9593A2C91609386100AB5EEB /* btQuickprof.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2401609386100AB5EEB /* btQuickprof.cpp */; }; - 9593A2CA1609386100AB5EEB /* btSerializer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2441609386100AB5EEB /* btSerializer.cpp */; }; - 9593A2CB1609386100AB5EEB /* Makefile.am in Resources */ = {isa = PBXBuildFile; fileRef = 9593A24A1609386100AB5EEB /* Makefile.am */; }; - 9593A2F91609388C00AB5EEB /* aclocal.m4 in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2CF1609388C00AB5EEB /* aclocal.m4 */; }; - 9593A2FA1609388C00AB5EEB /* callbacks.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2D01609388C00AB5EEB /* callbacks.c */; }; - 9593A2FB1609388C00AB5EEB /* ChangeLog in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2D11609388C00AB5EEB /* ChangeLog */; }; - 9593A2FC1609388C00AB5EEB /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2D21609388C00AB5EEB /* CMakeLists.txt */; }; - 9593A2FD1609388C00AB5EEB /* compress.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2D31609388C00AB5EEB /* compress.c */; }; - 9593A2FE1609388C00AB5EEB /* configure in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2D41609388C00AB5EEB /* configure */; }; - 9593A2FF1609388C00AB5EEB /* configure.in in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2D51609388C00AB5EEB /* configure.in */; }; - 9593A3001609388C00AB5EEB /* depcomp in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2D61609388C00AB5EEB /* depcomp */; }; - 9593A3011609388C00AB5EEB /* design.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2D71609388C00AB5EEB /* design.txt */; }; - 9593A3081609388C00AB5EEB /* Doxyfile in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2DF1609388C00AB5EEB /* Doxyfile */; }; - 9593A3091609388C00AB5EEB /* enet.dsp in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2E01609388C00AB5EEB /* enet.dsp */; }; - 9593A30A1609388C00AB5EEB /* host.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2E11609388C00AB5EEB /* host.c */; }; - 9593A30B1609388C00AB5EEB /* install-sh in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2ED1609388C00AB5EEB /* install-sh */; }; - 9593A30C1609388C00AB5EEB /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2EE1609388C00AB5EEB /* LICENSE */; }; - 9593A30D1609388C00AB5EEB /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2EF1609388C00AB5EEB /* list.c */; }; - 9593A30E1609388C00AB5EEB /* Makefile.am in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2F01609388C00AB5EEB /* Makefile.am */; }; - 9593A30F1609388C00AB5EEB /* missing in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2F11609388C00AB5EEB /* missing */; }; - 9593A3101609388C00AB5EEB /* packet.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2F21609388C00AB5EEB /* packet.c */; }; - 9593A3111609388C00AB5EEB /* peer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2F31609388C00AB5EEB /* peer.c */; }; - 9593A3121609388C00AB5EEB /* protocol.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2F41609388C00AB5EEB /* protocol.c */; }; - 9593A3131609388C00AB5EEB /* README in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2F51609388C00AB5EEB /* README */; }; - 9593A3141609388C00AB5EEB /* tutorial.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9593A2F61609388C00AB5EEB /* tutorial.txt */; }; - 9593A3151609388C00AB5EEB /* unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2F71609388C00AB5EEB /* unix.c */; }; - 9593A3161609388C00AB5EEB /* win32.c in Sources */ = {isa = PBXBuildFile; fileRef = 9593A2F81609388C00AB5EEB /* win32.c */; }; - 9593A32016093A7300AB5EEB /* ai_properties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9593A31E16093A7300AB5EEB /* ai_properties.cpp */; }; - 959482D310EBC0790031BADF /* track_object_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 959482CF10EBC0790031BADF /* track_object_manager.cpp */; }; - 959482D410EBC0790031BADF /* track_object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 959482D110EBC0790031BADF /* track_object.cpp */; }; - 9598A5B613CFBA83000B83EA /* hardware_skinning.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9598A5B413CFBA83000B83EA /* hardware_skinning.cpp */; }; - 959DE0C613C297B90068ED78 /* swatter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 959DE0C413C297B90068ED78 /* swatter.cpp */; }; - 95A0BA2413E63F6700620EA6 /* kart_with_stats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A0BA2213E63F6700620EA6 /* kart_with_stats.cpp */; }; - 95A1966214FC422D0074B892 /* ghost_kart.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A1966014FC422D0074B892 /* ghost_kart.cpp */; }; - 95A1978E1502E5020074B892 /* replay_base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A197881502E5020074B892 /* replay_base.cpp */; }; - 95A1978F1502E5020074B892 /* replay_play.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A1978A1502E5020074B892 /* replay_play.cpp */; }; - 95A197901502E5020074B892 /* replay_recorder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A1978C1502E5020074B892 /* replay_recorder.cpp */; }; - 95A197951502E5320074B892 /* skidding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95A197931502E5320074B892 /* skidding.cpp */; }; - 95AA4C67148AF2CC0053771D /* lod_node_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95AA4C65148AF2CC0053771D /* lod_node_loader.cpp */; }; - 95B0E8A816A4DC060037391C /* log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95B0E8A616A4DC060037391C /* log.cpp */; }; - 95B0E8B016A4DC390037391C /* tgt_log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95B0E8AE16A4DC390037391C /* tgt_log.cpp */; }; - 95B0E8B716A4DC9E0037391C /* easter_egg_hunt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95B0E8B316A4DC9E0037391C /* easter_egg_hunt.cpp */; }; - 95B0E8B816A4DC9E0037391C /* tutorial_world.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95B0E8B516A4DC9E0037391C /* tutorial_world.cpp */; }; - 95B5CD14102DE08F00EF2001 /* device_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95B5CD13102DE08F00EF2001 /* device_config.cpp */; }; - 95BF1E68127513A100F78AE7 /* max_speed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95BF1E66127513A100F78AE7 /* max_speed.cpp */; }; - 95C631A4142AC79500416D47 /* leak_check.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C631A3142AC79500416D47 /* leak_check.cpp */; }; - 95C77D631069589B0080838E /* ambient_light_sphere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C77D621069589B0080838E /* ambient_light_sphere.cpp */; }; - 95C77D66106958A50080838E /* check_line.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C77D65106958A50080838E /* check_line.cpp */; }; - 95C77D6F106958E10080838E /* check_sphere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C77D6D106958E10080838E /* check_sphere.cpp */; }; - 95C9C97210E93456005A418D /* stars.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95C9C97010E93456005A418D /* stars.cpp */; }; - 95CB476C0FF30EF400413BAE /* bezier_curve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95CB476B0FF30EF400413BAE /* bezier_curve.cpp */; }; - 95D2343F1078227A00625256 /* feature_unlocked.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95D2343E1078227A00625256 /* feature_unlocked.cpp */; }; - 95D2C43013D6605600E84032 /* rubber_ball.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95D2C42E13D6605600E84032 /* rubber_ball.cpp */; }; - 95D69A7415226E4700D598D8 /* abstract_kart.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95D69A7215226E4700D598D8 /* abstract_kart.cpp */; }; - 95D71F8B16BF380900C0F691 /* classic.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F6416BF380900C0F691 /* classic.c */; }; - 95D71F8C16BF380900C0F691 /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 95D71F6616BF380900C0F691 /* CMakeLists.txt */; }; - 95D71F8D16BF380900C0F691 /* dynamics.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F6916BF380900C0F691 /* dynamics.c */; }; - 95D71F8E16BF380900C0F691 /* events.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F6B16BF380900C0F691 /* events.c */; }; - 95D71F8F16BF380900C0F691 /* guitar_hero_3.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F6D16BF380900C0F691 /* guitar_hero_3.c */; }; - 95D71F9016BF380900C0F691 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F6F16BF380900C0F691 /* io.c */; }; - 95D71F9216BF380900C0F691 /* io_win.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7216BF380900C0F691 /* io_win.c */; }; - 95D71F9316BF380900C0F691 /* ir.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7316BF380900C0F691 /* ir.c */; }; - 95D71F9416BF380900C0F691 /* motion_plus.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7516BF380900C0F691 /* motion_plus.c */; }; - 95D71F9516BF380900C0F691 /* nunchuk.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7716BF380900C0F691 /* nunchuk.c */; }; - 95D71F9616BF380900C0F691 /* os_mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7C16BF380900C0F691 /* os_mac.m */; }; - 95D71F9716BF380900C0F691 /* os_mac_find.m in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7D16BF380900C0F691 /* os_mac_find.m */; }; - 95D71F9816BF380900C0F691 /* os_mac_interface.m in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7E16BF380900C0F691 /* os_mac_interface.m */; }; - 95D71F9916BF380900C0F691 /* os_nix.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F7F16BF380900C0F691 /* os_nix.c */; }; - 95D71F9A16BF380900C0F691 /* os_win.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F8016BF380900C0F691 /* os_win.c */; }; - 95D71F9B16BF380900C0F691 /* README in Resources */ = {isa = PBXBuildFile; fileRef = 95D71F8116BF380900C0F691 /* README */; }; - 95D71F9C16BF380900C0F691 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F8216BF380900C0F691 /* util.c */; }; - 95D71F9D16BF380900C0F691 /* wiiboard.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F8316BF380900C0F691 /* wiiboard.c */; }; - 95D71F9E16BF380900C0F691 /* wiiuse.vcproj in Resources */ = {isa = PBXBuildFile; fileRef = 95D71F8616BF380900C0F691 /* wiiuse.vcproj */; }; - 95D71F9F16BF380900C0F691 /* wiiuse.c in Sources */ = {isa = PBXBuildFile; fileRef = 95D71F8716BF380900C0F691 /* wiiuse.c */; }; - 95D71FA916BF3A4F00C0F691 /* IOBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95D71FA816BF3A4F00C0F691 /* IOBluetooth.framework */; }; - 95D71FB216BF3A9E00C0F691 /* wiimote_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95D71FB016BF3A9E00C0F691 /* wiimote_manager.cpp */; }; - 95D71FB716BF3C7800C0F691 /* tutorial_message_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95D71FB516BF3C7800C0F691 /* tutorial_message_dialog.cpp */; }; - 95D950D20FE473CA002E10AD /* stk_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95D950CE0FE473CA002E10AD /* stk_config.cpp */; }; - 95D950D30FE473CA002E10AD /* user_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95D950D00FE473CA002E10AD /* user_config.cpp */; }; - 95DFC5021106933B00A043A9 /* slip_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95DFC5001106933B00A043A9 /* slip_stream.cpp */; }; - 95E1FCDF130369EB004D83CC /* per_camera_node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E1FCDD130369EB004D83CC /* per_camera_node.cpp */; }; - 95E246BE111A2959000C965D /* confirm_resolution_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E246BC111A2959000C965D /* confirm_resolution_dialog.cpp */; }; - 95E5C318148C17E500AD3FCC /* story_mode_lobby.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5C316148C17E500AD3FCC /* story_mode_lobby.cpp */; }; - 95E5C335148C19F500AD3FCC /* story_mode_new.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5C333148C19F500AD3FCC /* story_mode_new.cpp */; }; - 95E5C3C2148C418100AD3FCC /* game_slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E5C3C0148C418100AD3FCC /* game_slot.cpp */; }; - 95E6A0C011DCF37800AE088A /* addons_loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95E6A0BE11DCF37800AE088A /* addons_loading.cpp */; }; - 95ECA10010124C5000D47C5F /* button_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0EC10124C5000D47C5F /* button_widget.cpp */; }; - 95ECA10110124C5000D47C5F /* check_box_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0EE10124C5000D47C5F /* check_box_widget.cpp */; }; - 95ECA10210124C5000D47C5F /* icon_button_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0F010124C5000D47C5F /* icon_button_widget.cpp */; }; - 95ECA10310124C5000D47C5F /* label_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0F210124C5000D47C5F /* label_widget.cpp */; }; - 95ECA10410124C5000D47C5F /* list_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0F410124C5000D47C5F /* list_widget.cpp */; }; - 95ECA10510124C5000D47C5F /* model_view_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0F610124C5000D47C5F /* model_view_widget.cpp */; }; - 95ECA10710124C5000D47C5F /* ribbon_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0FA10124C5000D47C5F /* ribbon_widget.cpp */; }; - 95ECA10810124C5000D47C5F /* spinner_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0FC10124C5000D47C5F /* spinner_widget.cpp */; }; - 95ECA10910124C5000D47C5F /* text_box_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95ECA0FE10124C5000D47C5F /* text_box_widget.cpp */; }; - 95EF178E14AFBC91005FFEEB /* race_gui_overworld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95EF178C14AFBC91005FFEEB /* race_gui_overworld.cpp */; }; - 95EF1E0A1506D6CF0041AC79 /* skidding_properties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95EF1E081506D6CF0041AC79 /* skidding_properties.cpp */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9507E9E70FC1CDDF00BD2B92 /* Copy frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 95591C4412A5E91E00FB1E95 /* fribidi.framework in Copy frameworks */, - 9507E9DB0FC1CDD500BD2B92 /* Vorbis.framework in Copy frameworks */, - 9507E9D10FC1CDCE00BD2B92 /* Ogg.framework in Copy frameworks */, - 9507E9D20FC1CDCE00BD2B92 /* OpenAL.framework in Copy frameworks */, - 9527AA4513CFC345009188DB /* IrrFramework.framework in Copy frameworks */, - ); - name = "Copy frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 95017B3F124698C400C90D56 /* help_screen_4.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = help_screen_4.cpp; path = ../../states_screens/help_screen_4.cpp; sourceTree = SOURCE_ROOT; }; - 95017B40124698C400C90D56 /* help_screen_4.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = help_screen_4.hpp; path = ../../states_screens/help_screen_4.hpp; sourceTree = SOURCE_ROOT; }; - 9505570E0F6963790056E88C /* file_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = file_manager.cpp; path = ../../io/file_manager.cpp; sourceTree = SOURCE_ROOT; }; - 9505570F0F6963790056E88C /* file_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = file_manager.hpp; path = ../../io/file_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9505574E0F69684D0056E88C /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = /System/Library/Frameworks/AudioUnit.framework; sourceTree = ""; }; - 950557570F6968860056E88C /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; - 9505575F0F6968A50056E88C /* QuickTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickTime.framework; path = /System/Library/Frameworks/QuickTime.framework; sourceTree = ""; }; - 950557640F6968BE0056E88C /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - 9507E9B60FC1CCE900BD2B92 /* stk.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = stk.icns; sourceTree = SOURCE_ROOT; }; - 950D448A118DEE3C006CFC41 /* CGUISpriteBank.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CGUISpriteBank.cpp; path = ../../guiengine/CGUISpriteBank.cpp; sourceTree = SOURCE_ROOT; }; - 950D448B118DEE3C006CFC41 /* CGUISpriteBank.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CGUISpriteBank.h; path = ../../guiengine/CGUISpriteBank.h; sourceTree = SOURCE_ROOT; }; - 950D45CF118E040E006CFC41 /* options_screen_input2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options_screen_input2.cpp; path = ../../states_screens/options_screen_input2.cpp; sourceTree = SOURCE_ROOT; }; - 950D45D0118E040E006CFC41 /* options_screen_input2.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options_screen_input2.hpp; path = ../../states_screens/options_screen_input2.hpp; sourceTree = SOURCE_ROOT; }; - 9516B07C12629C4E005F9493 /* sfx_buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sfx_buffer.cpp; path = ../../audio/sfx_buffer.cpp; sourceTree = SOURCE_ROOT; }; - 9516B07D12629C4E005F9493 /* sfx_buffer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sfx_buffer.hpp; path = ../../audio/sfx_buffer.hpp; sourceTree = SOURCE_ROOT; }; - 951B50AD12C9698B004F6993 /* xml_writer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = xml_writer.cpp; path = ../../io/xml_writer.cpp; sourceTree = SOURCE_ROOT; }; - 951B50AF12C96A13004F6993 /* xml_writer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = xml_writer.hpp; path = ../../io/xml_writer.hpp; sourceTree = SOURCE_ROOT; }; - 951BC65C0FFAF290006B5FF1 /* ipo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ipo.cpp; path = ../../animations/ipo.cpp; sourceTree = SOURCE_ROOT; }; - 951BC65D0FFAF290006B5FF1 /* ipo.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ipo.hpp; path = ../../animations/ipo.hpp; sourceTree = SOURCE_ROOT; }; - 951C357D0FC05BF400A48379 /* quad_set.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = quad_set.hpp; path = ../../tracks/quad_set.hpp; sourceTree = SOURCE_ROOT; }; - 951C357E0FC05BF400A48379 /* quad_set.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = quad_set.cpp; path = ../../tracks/quad_set.cpp; sourceTree = SOURCE_ROOT; }; - 951C357F0FC05BF400A48379 /* quad_graph.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = quad_graph.hpp; path = ../../tracks/quad_graph.hpp; sourceTree = SOURCE_ROOT; }; - 951C35800FC05BF400A48379 /* quad_graph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = quad_graph.cpp; path = ../../tracks/quad_graph.cpp; sourceTree = SOURCE_ROOT; }; - 9522F123107948AD0067ECF5 /* main_menu_screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = main_menu_screen.hpp; path = ../../states_screens/main_menu_screen.hpp; sourceTree = SOURCE_ROOT; }; - 9522F124107948AD0067ECF5 /* main_menu_screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main_menu_screen.cpp; path = ../../states_screens/main_menu_screen.cpp; sourceTree = SOURCE_ROOT; }; - 9522F159107949780067ECF5 /* race_setup_screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_setup_screen.hpp; path = ../../states_screens/race_setup_screen.hpp; sourceTree = SOURCE_ROOT; }; - 9522F15A107949780067ECF5 /* race_setup_screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_setup_screen.cpp; path = ../../states_screens/race_setup_screen.cpp; sourceTree = SOURCE_ROOT; }; - 9522F15C10794A350067ECF5 /* tracks_screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tracks_screen.cpp; path = ../../states_screens/tracks_screen.cpp; sourceTree = SOURCE_ROOT; }; - 9522F15D10794A350067ECF5 /* tracks_screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = tracks_screen.hpp; path = ../../states_screens/tracks_screen.hpp; sourceTree = SOURCE_ROOT; }; - 9522F1DE10795E8A0067ECF5 /* help_screen_1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = help_screen_1.cpp; path = ../../states_screens/help_screen_1.cpp; sourceTree = SOURCE_ROOT; }; - 9522F1DF10795E8A0067ECF5 /* help_screen_1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = help_screen_1.hpp; path = ../../states_screens/help_screen_1.hpp; sourceTree = SOURCE_ROOT; }; - 9522F1E110795EFF0067ECF5 /* help_screen_2.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = help_screen_2.hpp; path = ../../states_screens/help_screen_2.hpp; sourceTree = SOURCE_ROOT; }; - 9522F1E210795EFF0067ECF5 /* help_screen_2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = help_screen_2.cpp; path = ../../states_screens/help_screen_2.cpp; sourceTree = SOURCE_ROOT; }; - 9522F1E310795EFF0067ECF5 /* help_screen_3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = help_screen_3.hpp; path = ../../states_screens/help_screen_3.hpp; sourceTree = SOURCE_ROOT; }; - 9522F1E410795EFF0067ECF5 /* help_screen_3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = help_screen_3.cpp; path = ../../states_screens/help_screen_3.cpp; sourceTree = SOURCE_ROOT; }; - 9522F1EB107961560067ECF5 /* options_screen_input.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options_screen_input.cpp; path = ../../states_screens/options_screen_input.cpp; sourceTree = SOURCE_ROOT; }; - 9522F1EC107961560067ECF5 /* options_screen_players.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options_screen_players.hpp; path = ../../states_screens/options_screen_players.hpp; sourceTree = SOURCE_ROOT; }; - 9522F1ED107961560067ECF5 /* options_screen_players.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options_screen_players.cpp; path = ../../states_screens/options_screen_players.cpp; sourceTree = SOURCE_ROOT; }; - 9522F1EE107961560067ECF5 /* options_screen_input.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options_screen_input.hpp; path = ../../states_screens/options_screen_input.hpp; sourceTree = SOURCE_ROOT; }; - 9524739410497C75000C197E /* dynamic_ribbon_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = dynamic_ribbon_widget.hpp; path = ../../guiengine/widgets/dynamic_ribbon_widget.hpp; sourceTree = SOURCE_ROOT; }; - 9524739510497C75000C197E /* dynamic_ribbon_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dynamic_ribbon_widget.cpp; path = ../../guiengine/widgets/dynamic_ribbon_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95251F3B12554AB200505BA5 /* check_lap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check_lap.cpp; path = ../../tracks/check_lap.cpp; sourceTree = SOURCE_ROOT; }; - 95251F3C12554AB200505BA5 /* check_lap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = check_lap.hpp; path = ../../tracks/check_lap.hpp; sourceTree = SOURCE_ROOT; }; - 9525B71211C851D30094BD96 /* CBatchingMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CBatchingMesh.cpp; path = ../../graphics/CBatchingMesh.cpp; sourceTree = SOURCE_ROOT; }; - 9525B71311C851D30094BD96 /* CBatchingMesh.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CBatchingMesh.hpp; path = ../../graphics/CBatchingMesh.hpp; sourceTree = SOURCE_ROOT; }; - 95263DE00FD7471900CF5F92 /* grand_prix_data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = grand_prix_data.cpp; path = ../../race/grand_prix_data.cpp; sourceTree = SOURCE_ROOT; }; - 95263DE10FD7471900CF5F92 /* grand_prix_data.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = grand_prix_data.hpp; path = ../../race/grand_prix_data.hpp; sourceTree = SOURCE_ROOT; }; - 95263DE20FD7471900CF5F92 /* grand_prix_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = grand_prix_manager.cpp; path = ../../race/grand_prix_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95263DE30FD7471900CF5F92 /* grand_prix_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = grand_prix_manager.hpp; path = ../../race/grand_prix_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95263DE40FD7471900CF5F92 /* highscore_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = highscore_manager.cpp; path = ../../race/highscore_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95263DE50FD7471900CF5F92 /* highscore_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = highscore_manager.hpp; path = ../../race/highscore_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95263DE60FD7471900CF5F92 /* highscores.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = highscores.cpp; path = ../../race/highscores.cpp; sourceTree = SOURCE_ROOT; }; - 95263DE70FD7471900CF5F92 /* highscores.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = highscores.hpp; path = ../../race/highscores.hpp; sourceTree = SOURCE_ROOT; }; - 95263DE80FD7471900CF5F92 /* history.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = history.cpp; path = ../../race/history.cpp; sourceTree = SOURCE_ROOT; }; - 95263DE90FD7471900CF5F92 /* history.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = history.hpp; path = ../../race/history.hpp; sourceTree = SOURCE_ROOT; }; - 95263DEA0FD7471900CF5F92 /* race_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_manager.cpp; path = ../../race/race_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95263DEB0FD7471900CF5F92 /* race_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_manager.hpp; path = ../../race/race_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9528C71412D69494006E9167 /* particle_kind_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = particle_kind_manager.cpp; path = ../../graphics/particle_kind_manager.cpp; sourceTree = SOURCE_ROOT; }; - 9528C71512D69494006E9167 /* particle_kind_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = particle_kind_manager.hpp; path = ../../graphics/particle_kind_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9528CC221291E7A10078A5EF /* binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = binding.cpp; path = ../../input/binding.cpp; sourceTree = SOURCE_ROOT; }; - 9528CC231291E7A10078A5EF /* binding.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = binding.hpp; path = ../../input/binding.hpp; sourceTree = SOURCE_ROOT; }; - 952997BF15B205DE0028301A /* cutscene_world.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cutscene_world.cpp; path = ../../modes/cutscene_world.cpp; sourceTree = SOURCE_ROOT; }; - 952997C015B205DE0028301A /* cutscene_world.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = cutscene_world.hpp; path = ../../modes/cutscene_world.hpp; sourceTree = SOURCE_ROOT; }; - 952997C115B205DE0028301A /* demo_world.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = demo_world.cpp; path = ../../modes/demo_world.cpp; sourceTree = SOURCE_ROOT; }; - 952997C215B205DE0028301A /* demo_world.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = demo_world.hpp; path = ../../modes/demo_world.hpp; sourceTree = SOURCE_ROOT; }; - 952997C315B205DE0028301A /* game_tutorial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = game_tutorial.cpp; path = ../../modes/game_tutorial.cpp; sourceTree = SOURCE_ROOT; }; - 952997C415B205DE0028301A /* game_tutorial.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = game_tutorial.hpp; path = ../../modes/game_tutorial.hpp; sourceTree = SOURCE_ROOT; }; - 952997EA15B206890028301A /* skidding_ai.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = skidding_ai.cpp; path = ../../karts/controller/skidding_ai.cpp; sourceTree = SOURCE_ROOT; }; - 952997EB15B206890028301A /* skidding_ai.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = skidding_ai.hpp; path = ../../karts/controller/skidding_ai.hpp; sourceTree = SOURCE_ROOT; }; - 952997F315B206D90028301A /* abstract_kart_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = abstract_kart_animation.cpp; path = ../../karts/abstract_kart_animation.cpp; sourceTree = SOURCE_ROOT; }; - 952997F415B206D90028301A /* abstract_kart_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = abstract_kart_animation.hpp; path = ../../karts/abstract_kart_animation.hpp; sourceTree = SOURCE_ROOT; }; - 952997F515B206D90028301A /* cannon_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cannon_animation.cpp; path = ../../karts/cannon_animation.cpp; sourceTree = SOURCE_ROOT; }; - 952997F615B206D90028301A /* cannon_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = cannon_animation.hpp; path = ../../karts/cannon_animation.hpp; sourceTree = SOURCE_ROOT; }; - 952997F715B206D90028301A /* explosion_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = explosion_animation.cpp; path = ../../karts/explosion_animation.cpp; sourceTree = SOURCE_ROOT; }; - 952997F815B206D90028301A /* explosion_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = explosion_animation.hpp; path = ../../karts/explosion_animation.hpp; sourceTree = SOURCE_ROOT; }; - 952997F915B206D90028301A /* rescue_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rescue_animation.cpp; path = ../../karts/rescue_animation.cpp; sourceTree = SOURCE_ROOT; }; - 952997FA15B206D90028301A /* rescue_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rescue_animation.hpp; path = ../../karts/rescue_animation.hpp; sourceTree = SOURCE_ROOT; }; - 9529980115B2070C0028301A /* cutscene_gui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cutscene_gui.cpp; path = ../../states_screens/cutscene_gui.cpp; sourceTree = SOURCE_ROOT; }; - 9529980215B2070C0028301A /* cutscene_gui.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = cutscene_gui.hpp; path = ../../states_screens/cutscene_gui.hpp; sourceTree = SOURCE_ROOT; }; - 9529980615B2072E0028301A /* player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = player.cpp; path = ../../config/player.cpp; sourceTree = SOURCE_ROOT; }; - 9529980A15B207450028301A /* check_cannon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check_cannon.cpp; path = ../../tracks/check_cannon.cpp; sourceTree = SOURCE_ROOT; }; - 9529980B15B207450028301A /* check_cannon.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = check_cannon.hpp; path = ../../tracks/check_cannon.hpp; sourceTree = SOURCE_ROOT; }; - 9529980F15B207740028301A /* show_curve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = show_curve.cpp; path = ../../graphics/show_curve.cpp; sourceTree = SOURCE_ROOT; }; - 9529981015B207740028301A /* show_curve.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = show_curve.hpp; path = ../../graphics/show_curve.hpp; sourceTree = SOURCE_ROOT; }; - 952A152D103F66D600B1895D /* camera.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = camera.cpp; path = ../../graphics/camera.cpp; sourceTree = SOURCE_ROOT; }; - 952A152E103F66D600B1895D /* camera.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = camera.hpp; path = ../../graphics/camera.hpp; sourceTree = SOURCE_ROOT; }; - 952A152F103F66D600B1895D /* explosion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = explosion.cpp; path = ../../graphics/explosion.cpp; sourceTree = SOURCE_ROOT; }; - 952A1530103F66D600B1895D /* explosion.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = explosion.hpp; path = ../../graphics/explosion.hpp; sourceTree = SOURCE_ROOT; }; - 952A1531103F66D600B1895D /* irr_driver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = irr_driver.cpp; path = ../../graphics/irr_driver.cpp; sourceTree = SOURCE_ROOT; }; - 952A1532103F66D600B1895D /* irr_driver.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = irr_driver.hpp; path = ../../graphics/irr_driver.hpp; sourceTree = SOURCE_ROOT; }; - 952A1533103F66D600B1895D /* material.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = material.cpp; path = ../../graphics/material.cpp; sourceTree = SOURCE_ROOT; }; - 952A1534103F66D600B1895D /* material.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = material.hpp; path = ../../graphics/material.hpp; sourceTree = SOURCE_ROOT; }; - 952A1535103F66D600B1895D /* material_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = material_manager.cpp; path = ../../graphics/material_manager.cpp; sourceTree = SOURCE_ROOT; }; - 952A1536103F66D600B1895D /* material_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = material_manager.hpp; path = ../../graphics/material_manager.hpp; sourceTree = SOURCE_ROOT; }; - 952A1537103F66D600B1895D /* mesh_tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mesh_tools.cpp; path = ../../graphics/mesh_tools.cpp; sourceTree = SOURCE_ROOT; }; - 952A1538103F66D600B1895D /* mesh_tools.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = mesh_tools.hpp; path = ../../graphics/mesh_tools.hpp; sourceTree = SOURCE_ROOT; }; - 952A1539103F66D600B1895D /* moving_texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moving_texture.cpp; path = ../../graphics/moving_texture.cpp; sourceTree = SOURCE_ROOT; }; - 952A153A103F66D600B1895D /* moving_texture.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = moving_texture.hpp; path = ../../graphics/moving_texture.hpp; sourceTree = SOURCE_ROOT; }; - 952A153D103F66D600B1895D /* shadow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = shadow.cpp; path = ../../graphics/shadow.cpp; sourceTree = SOURCE_ROOT; }; - 952A153E103F66D600B1895D /* shadow.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = shadow.hpp; path = ../../graphics/shadow.hpp; sourceTree = SOURCE_ROOT; }; - 952A153F103F66D600B1895D /* skid_marks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = skid_marks.cpp; path = ../../graphics/skid_marks.cpp; sourceTree = SOURCE_ROOT; }; - 952A1540103F66D600B1895D /* skid_marks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = skid_marks.hpp; path = ../../graphics/skid_marks.hpp; sourceTree = SOURCE_ROOT; }; - 952A1552103F68D000B1895D /* profile_world.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = profile_world.cpp; path = ../../modes/profile_world.cpp; sourceTree = SOURCE_ROOT; }; - 952A1553103F68D000B1895D /* profile_world.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = profile_world.hpp; path = ../../modes/profile_world.hpp; sourceTree = SOURCE_ROOT; }; - 95376CAD1320784100C842A4 /* lod_node.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lod_node.cpp; path = ../../graphics/lod_node.cpp; sourceTree = SOURCE_ROOT; }; - 95376CAE1320784100C842A4 /* lod_node.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = lod_node.hpp; path = ../../graphics/lod_node.hpp; sourceTree = SOURCE_ROOT; }; - 953789710FC7829100DD1F8E /* graph_node.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = graph_node.hpp; path = ../../tracks/graph_node.hpp; sourceTree = SOURCE_ROOT; }; - 953789720FC7829100DD1F8E /* graph_node.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = graph_node.cpp; path = ../../tracks/graph_node.cpp; sourceTree = SOURCE_ROOT; }; - 953789800FC7831400DD1F8E /* quad.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = quad.hpp; path = ../../tracks/quad.hpp; sourceTree = SOURCE_ROOT; }; - 953789810FC7831400DD1F8E /* quad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = quad.cpp; path = ../../tracks/quad.cpp; sourceTree = SOURCE_ROOT; }; - 9538A55E12CD094200CE3220 /* addon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = addon.cpp; path = ../../addons/addon.cpp; sourceTree = SOURCE_ROOT; }; - 9538A55F12CD094200CE3220 /* addon.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = addon.hpp; path = ../../addons/addon.hpp; sourceTree = SOURCE_ROOT; }; - 9538E2B512C25D6800172896 /* addons_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = addons_manager.cpp; path = ../../addons/addons_manager.cpp; sourceTree = SOURCE_ROOT; }; - 9538E2B612C25D6800172896 /* addons_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = addons_manager.hpp; path = ../../addons/addons_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9538E2B712C25D6800172896 /* network_http.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = network_http.cpp; path = ../../addons/network_http.cpp; sourceTree = SOURCE_ROOT; }; - 9538E2B812C25D6800172896 /* network_http.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = network_http.hpp; path = ../../addons/network_http.hpp; sourceTree = SOURCE_ROOT; }; - 9538E2E812C2682B00172896 /* IrrFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IrrFramework.framework; path = /Library/Frameworks/IrrFramework.framework; sourceTree = ""; }; - 95395A75129DFE130079BCE7 /* message_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = message_dialog.cpp; path = ../../states_screens/dialogs/message_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 95395A76129DFE130079BCE7 /* message_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = message_dialog.hpp; path = ../../states_screens/dialogs/message_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 953A959B13367D6E00D86B4D /* options_screen_ui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options_screen_ui.cpp; path = ../../states_screens/options_screen_ui.cpp; sourceTree = SOURCE_ROOT; }; - 953A959C13367D6E00D86B4D /* options_screen_ui.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options_screen_ui.hpp; path = ../../states_screens/options_screen_ui.hpp; sourceTree = SOURCE_ROOT; }; - 953EAAAD0F30A4220000D57D /* terrain_info.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = terrain_info.hpp; path = ../../tracks/terrain_info.hpp; sourceTree = SOURCE_ROOT; }; - 953EAAAE0F30A4220000D57D /* terrain_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = terrain_info.cpp; path = ../../tracks/terrain_info.cpp; sourceTree = SOURCE_ROOT; }; - 953EAAB00F30A4410000D57D /* triangle_mesh.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = triangle_mesh.hpp; path = ../../physics/triangle_mesh.hpp; sourceTree = SOURCE_ROOT; }; - 953EAAB10F30A4410000D57D /* triangle_mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = triangle_mesh.cpp; path = ../../physics/triangle_mesh.cpp; sourceTree = SOURCE_ROOT; }; - 953EAAB40F30A4650000D57D /* translation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = translation.hpp; path = ../../utils/translation.hpp; sourceTree = SOURCE_ROOT; }; - 953EAAB50F30A4650000D57D /* translation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = translation.cpp; path = ../../utils/translation.cpp; sourceTree = SOURCE_ROOT; }; - 953F8B1F11F7C13C00205E66 /* scalable_font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = scalable_font.cpp; path = ../../guiengine/scalable_font.cpp; sourceTree = SOURCE_ROOT; }; - 953F8B2011F7C13C00205E66 /* scalable_font.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = scalable_font.hpp; path = ../../guiengine/scalable_font.hpp; sourceTree = SOURCE_ROOT; }; - 9540E2560FD5F8FD002985B8 /* ptr_vector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ptr_vector.hpp; path = ../../utils/ptr_vector.hpp; sourceTree = SOURCE_ROOT; }; - 9540E2570FD5F8FD002985B8 /* no_copy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = no_copy.hpp; path = ../../utils/no_copy.hpp; sourceTree = SOURCE_ROOT; }; - 9542FC7512D3BDB000C00366 /* particle_emitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = particle_emitter.cpp; path = ../../graphics/particle_emitter.cpp; sourceTree = SOURCE_ROOT; }; - 9542FC7612D3BDB000C00366 /* particle_emitter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = particle_emitter.hpp; path = ../../graphics/particle_emitter.hpp; sourceTree = SOURCE_ROOT; }; - 9542FD4A12D3E0D700C00366 /* particle_kind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = particle_kind.cpp; path = ../../graphics/particle_kind.cpp; sourceTree = SOURCE_ROOT; }; - 9542FD4B12D3E0D700C00366 /* particle_kind.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = particle_kind.hpp; path = ../../graphics/particle_kind.hpp; sourceTree = SOURCE_ROOT; }; - 9543D58D14D36EE3000B0888 /* kart_gfx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_gfx.cpp; path = ../../karts/kart_gfx.cpp; sourceTree = SOURCE_ROOT; }; - 9543D58E14D36EE3000B0888 /* kart_gfx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_gfx.hpp; path = ../../karts/kart_gfx.hpp; sourceTree = SOURCE_ROOT; }; - 9543D59D14D38831000B0888 /* select_challenge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = select_challenge.cpp; path = ../../states_screens/dialogs/select_challenge.cpp; sourceTree = SOURCE_ROOT; }; - 9543D5A514D3885F000B0888 /* select_challenge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = select_challenge.hpp; path = ../../states_screens/dialogs/select_challenge.hpp; sourceTree = SOURCE_ROOT; }; - 9545ABC811E3E38300D3C37A /* progress_bar_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = progress_bar_widget.cpp; path = ../../guiengine/widgets/progress_bar_widget.cpp; sourceTree = SOURCE_ROOT; }; - 9545ABC911E3E38300D3C37A /* progress_bar_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = progress_bar_widget.hpp; path = ../../guiengine/widgets/progress_bar_widget.hpp; sourceTree = SOURCE_ROOT; }; - 954E486911B19C4100B1DF63 /* fribidi.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = fribidi.framework; path = /Library/Frameworks/fribidi.framework; sourceTree = ""; }; - 954E4C250FF98B6E0047FE3E /* animation_base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = animation_base.cpp; path = ../../animations/animation_base.cpp; sourceTree = SOURCE_ROOT; }; - 954E4C260FF98B6E0047FE3E /* animation_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = animation_base.hpp; path = ../../animations/animation_base.hpp; sourceTree = SOURCE_ROOT; }; - 954E4C290FF98B6E0047FE3E /* billboard_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = billboard_animation.cpp; path = ../../animations/billboard_animation.cpp; sourceTree = SOURCE_ROOT; }; - 954E4C2A0FF98B6E0047FE3E /* billboard_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = billboard_animation.hpp; path = ../../animations/billboard_animation.hpp; sourceTree = SOURCE_ROOT; }; - 954E4C2B0FF98B6E0047FE3E /* three_d_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = three_d_animation.cpp; path = ../../animations/three_d_animation.cpp; sourceTree = SOURCE_ROOT; }; - 954E4C2C0FF98B6E0047FE3E /* three_d_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = three_d_animation.hpp; path = ../../animations/three_d_animation.hpp; sourceTree = SOURCE_ROOT; }; - 9551B26D11DC0D4D002DD140 /* addons_screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = addons_screen.cpp; path = ../../states_screens/addons_screen.cpp; sourceTree = SOURCE_ROOT; }; - 9551B26E11DC0D4D002DD140 /* addons_screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = addons_screen.hpp; path = ../../states_screens/addons_screen.hpp; sourceTree = SOURCE_ROOT; }; - 9551B27811DC0D6A002DD140 /* zip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = zip.cpp; path = ../../addons/zip.cpp; sourceTree = SOURCE_ROOT; }; - 9551B27911DC0D6A002DD140 /* zip.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = zip.hpp; path = ../../addons/zip.hpp; sourceTree = SOURCE_ROOT; }; - 9551C7F90FC1B63C00DB481B /* Ogg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ogg.framework; path = /Library/Frameworks/Ogg.framework; sourceTree = ""; }; - 9551C7FA0FC1B63C00DB481B /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = /Library/Frameworks/OpenAL.framework; sourceTree = ""; }; - 9551C7FB0FC1B63C00DB481B /* Vorbis.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Vorbis.framework; path = /Library/Frameworks/Vorbis.framework; sourceTree = ""; }; - 9551C8210FC1B6FF00DB481B /* SuperTuxKart.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SuperTuxKart.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 9551C9F80FC1B87600DB481B /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = SOURCE_ROOT; wrapsLines = 1; }; - 9551CA100FC1BB6400DB481B /* SuperTuxKart-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SuperTuxKart-Info.plist"; sourceTree = SOURCE_ROOT; }; - 9551CBD90FC1BB9200DB481B /* AGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AGL.framework; path = /System/Library/Frameworks/AGL.framework; sourceTree = ""; }; - 9552C1F91231249000347B6C /* world_with_rank.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = world_with_rank.cpp; path = ../../modes/world_with_rank.cpp; sourceTree = SOURCE_ROOT; }; - 9552C1FA1231249000347B6C /* world_with_rank.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = world_with_rank.hpp; path = ../../modes/world_with_rank.hpp; sourceTree = SOURCE_ROOT; }; - 9553823910FD4FEC00737979 /* constants.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = constants.cpp; path = ../../utils/constants.cpp; sourceTree = SOURCE_ROOT; }; - 9554C4A411F1188000906416 /* race_result_gui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_result_gui.cpp; path = ../../states_screens/race_result_gui.cpp; sourceTree = SOURCE_ROOT; }; - 9554C4A511F1188000906416 /* race_result_gui.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_result_gui.hpp; path = ../../states_screens/race_result_gui.hpp; sourceTree = SOURCE_ROOT; }; - 9556A87C119EF976009C558F /* options_screen_audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options_screen_audio.cpp; path = ../../states_screens/options_screen_audio.cpp; sourceTree = SOURCE_ROOT; }; - 9556A87D119EF976009C558F /* options_screen_audio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options_screen_audio.hpp; path = ../../states_screens/options_screen_audio.hpp; sourceTree = SOURCE_ROOT; }; - 9556A87E119EF976009C558F /* options_screen_video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options_screen_video.cpp; path = ../../states_screens/options_screen_video.cpp; sourceTree = SOURCE_ROOT; }; - 9556A87F119EF976009C558F /* options_screen_video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options_screen_video.hpp; path = ../../states_screens/options_screen_video.hpp; sourceTree = SOURCE_ROOT; }; - 955764E412FB67EF005CE479 /* btKartRaycast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btKartRaycast.cpp; path = ../../physics/btKartRaycast.cpp; sourceTree = SOURCE_ROOT; }; - 955764E512FB67EF005CE479 /* btKartRaycast.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = btKartRaycast.hpp; path = ../../physics/btKartRaycast.hpp; sourceTree = SOURCE_ROOT; }; - 9559DE7E12FF777600350DE8 /* rain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rain.cpp; path = ../../graphics/rain.cpp; sourceTree = SOURCE_ROOT; }; - 955DE88110042701006A4F3C /* check_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check_manager.cpp; path = ../../tracks/check_manager.cpp; sourceTree = SOURCE_ROOT; }; - 955DE88210042701006A4F3C /* check_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = check_manager.hpp; path = ../../tracks/check_manager.hpp; sourceTree = SOURCE_ROOT; }; - 955DE8871004273B006A4F3C /* check_structure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check_structure.cpp; path = ../../tracks/check_structure.cpp; sourceTree = SOURCE_ROOT; }; - 955DE8881004273B006A4F3C /* check_structure.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = check_structure.hpp; path = ../../tracks/check_structure.hpp; sourceTree = SOURCE_ROOT; }; - 9560368A12187EFB00EB96C4 /* layout_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = layout_manager.cpp; path = ../../guiengine/layout_manager.cpp; sourceTree = SOURCE_ROOT; }; - 9560368B12187EFB00EB96C4 /* layout_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = layout_manager.hpp; path = ../../guiengine/layout_manager.hpp; sourceTree = SOURCE_ROOT; }; - 956039B81218C09E00EB96C4 /* abstract_top_level_container.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = abstract_top_level_container.cpp; path = ../../guiengine/abstract_top_level_container.cpp; sourceTree = SOURCE_ROOT; }; - 956039B91218C09E00EB96C4 /* abstract_top_level_container.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = abstract_top_level_container.hpp; path = ../../guiengine/abstract_top_level_container.hpp; sourceTree = SOURCE_ROOT; }; - 95634EF01126272C009C145D /* gp_info_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gp_info_dialog.cpp; path = ../../states_screens/dialogs/gp_info_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 95634EF11126272C009C145D /* gp_info_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = gp_info_dialog.hpp; path = ../../states_screens/dialogs/gp_info_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 956541B910DD5F0A00C83E99 /* arenas_screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = arenas_screen.cpp; path = ../../states_screens/arenas_screen.cpp; sourceTree = SOURCE_ROOT; }; - 956541BA10DD5F0A00C83E99 /* arenas_screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = arenas_screen.hpp; path = ../../states_screens/arenas_screen.hpp; sourceTree = SOURCE_ROOT; }; - 956541DF10DD628C00C83E99 /* add_device_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = add_device_dialog.cpp; path = ../../states_screens/dialogs/add_device_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 956541E010DD628C00C83E99 /* add_device_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = add_device_dialog.hpp; path = ../../states_screens/dialogs/add_device_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 956830DE1132EC9E00088D14 /* irr_debug_drawer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = irr_debug_drawer.cpp; path = ../../physics/irr_debug_drawer.cpp; sourceTree = SOURCE_ROOT; }; - 956830DF1132EC9E00088D14 /* irr_debug_drawer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = irr_debug_drawer.hpp; path = ../../physics/irr_debug_drawer.hpp; sourceTree = SOURCE_ROOT; }; - 956B0A9E1232D2E900767CCD /* bubble_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bubble_widget.cpp; path = ../../guiengine/widgets/bubble_widget.cpp; sourceTree = SOURCE_ROOT; }; - 956B0AA21232D45D00767CCD /* bubble_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = bubble_widget.hpp; path = ../../guiengine/widgets/bubble_widget.hpp; sourceTree = SOURCE_ROOT; }; - 956C6EC81128D3FB004336C8 /* controller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = controller.cpp; path = ../../karts/controller/controller.cpp; sourceTree = SOURCE_ROOT; }; - 956C6EC91128D3FB004336C8 /* controller.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = controller.hpp; path = ../../karts/controller/controller.hpp; sourceTree = SOURCE_ROOT; }; - 956C6ECC1128D3FB004336C8 /* end_controller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = end_controller.cpp; path = ../../karts/controller/end_controller.cpp; sourceTree = SOURCE_ROOT; }; - 956C6ECD1128D3FB004336C8 /* end_controller.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = end_controller.hpp; path = ../../karts/controller/end_controller.hpp; sourceTree = SOURCE_ROOT; }; - 956C6ECE1128D3FB004336C8 /* kart_control.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_control.hpp; path = ../../karts/controller/kart_control.hpp; sourceTree = SOURCE_ROOT; }; - 956C6ED11128D3FB004336C8 /* player_controller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = player_controller.cpp; path = ../../karts/controller/player_controller.cpp; sourceTree = SOURCE_ROOT; }; - 956C6ED21128D3FB004336C8 /* player_controller.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = player_controller.hpp; path = ../../karts/controller/player_controller.hpp; sourceTree = SOURCE_ROOT; }; - 956F557313527E4B005C291D /* race_gui_base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_gui_base.cpp; path = ../../states_screens/race_gui_base.cpp; sourceTree = SOURCE_ROOT; }; - 956F557413527E4B005C291D /* race_gui_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_gui_base.hpp; path = ../../states_screens/race_gui_base.hpp; sourceTree = SOURCE_ROOT; }; - 95702AE51306076100EEC3A0 /* aabbox3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = aabbox3d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/aabbox3d.h; sourceTree = SOURCE_ROOT; }; - 95702AE61306076100EEC3A0 /* CDynamicMeshBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDynamicMeshBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/CDynamicMeshBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702AE71306076100EEC3A0 /* CIndexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CIndexBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/CIndexBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702AE81306076100EEC3A0 /* CMeshBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CMeshBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/CMeshBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702AE91306076100EEC3A0 /* coreutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = coreutil.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/coreutil.h; sourceTree = SOURCE_ROOT; }; - 95702AEA1306076100EEC3A0 /* CVertexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CVertexBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/CVertexBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702AEB1306076100EEC3A0 /* dimension2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dimension2d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/dimension2d.h; sourceTree = SOURCE_ROOT; }; - 95702AEC1306076100EEC3A0 /* driverChoice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = driverChoice.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/driverChoice.h; sourceTree = SOURCE_ROOT; }; - 95702AED1306076100EEC3A0 /* EAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EAttributes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EAttributes.h; sourceTree = SOURCE_ROOT; }; - 95702AEE1306076100EEC3A0 /* ECullingTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ECullingTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ECullingTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AEF1306076100EEC3A0 /* EDebugSceneTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EDebugSceneTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EDebugSceneTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AF01306076100EEC3A0 /* EDeviceTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EDeviceTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EDeviceTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AF11306076100EEC3A0 /* EDriverFeatures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EDriverFeatures.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EDriverFeatures.h; sourceTree = SOURCE_ROOT; }; - 95702AF21306076100EEC3A0 /* EDriverTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EDriverTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EDriverTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AF31306076100EEC3A0 /* EGUIAlignment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EGUIAlignment.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EGUIAlignment.h; sourceTree = SOURCE_ROOT; }; - 95702AF41306076100EEC3A0 /* EGUIElementTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EGUIElementTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EGUIElementTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AF51306076100EEC3A0 /* EHardwareBufferFlags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EHardwareBufferFlags.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EHardwareBufferFlags.h; sourceTree = SOURCE_ROOT; }; - 95702AF61306076100EEC3A0 /* EMaterialFlags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EMaterialFlags.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EMaterialFlags.h; sourceTree = SOURCE_ROOT; }; - 95702AF71306076100EEC3A0 /* EMaterialTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EMaterialTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EMaterialTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AF81306076100EEC3A0 /* EMeshWriterEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EMeshWriterEnums.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EMeshWriterEnums.h; sourceTree = SOURCE_ROOT; }; - 95702AF91306076100EEC3A0 /* EMessageBoxFlags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EMessageBoxFlags.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EMessageBoxFlags.h; sourceTree = SOURCE_ROOT; }; - 95702AFA1306076100EEC3A0 /* EPrimitiveTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EPrimitiveTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EPrimitiveTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AFB1306076100EEC3A0 /* ESceneNodeAnimatorTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ESceneNodeAnimatorTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ESceneNodeAnimatorTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AFC1306076100EEC3A0 /* ESceneNodeTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ESceneNodeTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ESceneNodeTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AFD1306076100EEC3A0 /* EShaderTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EShaderTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/EShaderTypes.h; sourceTree = SOURCE_ROOT; }; - 95702AFE1306076100EEC3A0 /* ETerrainElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ETerrainElements.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ETerrainElements.h; sourceTree = SOURCE_ROOT; }; - 95702AFF1306076100EEC3A0 /* fast_atof.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fast_atof.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/fast_atof.h; sourceTree = SOURCE_ROOT; }; - 95702B001306076100EEC3A0 /* heapsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = heapsort.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/heapsort.h; sourceTree = SOURCE_ROOT; }; - 95702B011306076100EEC3A0 /* IAnimatedMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAnimatedMesh.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IAnimatedMesh.h; sourceTree = SOURCE_ROOT; }; - 95702B021306076100EEC3A0 /* IAnimatedMeshMD2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAnimatedMeshMD2.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IAnimatedMeshMD2.h; sourceTree = SOURCE_ROOT; }; - 95702B031306076100EEC3A0 /* IAnimatedMeshMD3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAnimatedMeshMD3.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IAnimatedMeshMD3.h; sourceTree = SOURCE_ROOT; }; - 95702B041306076100EEC3A0 /* IAnimatedMeshSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAnimatedMeshSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IAnimatedMeshSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B051306076100EEC3A0 /* IAttributeExchangingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAttributeExchangingObject.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IAttributeExchangingObject.h; sourceTree = SOURCE_ROOT; }; - 95702B061306076100EEC3A0 /* IAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAttributes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IAttributes.h; sourceTree = SOURCE_ROOT; }; - 95702B071306076100EEC3A0 /* IBillboardSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IBillboardSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IBillboardSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B081306076100EEC3A0 /* IBillboardTextSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IBillboardTextSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IBillboardTextSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B091306076100EEC3A0 /* IBoneSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IBoneSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IBoneSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B0A1306076100EEC3A0 /* ICameraSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ICameraSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ICameraSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B0B1306076100EEC3A0 /* ICursorControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ICursorControl.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ICursorControl.h; sourceTree = SOURCE_ROOT; }; - 95702B0C1306076100EEC3A0 /* IDummyTransformationSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDummyTransformationSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IDummyTransformationSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B0D1306076100EEC3A0 /* IDynamicMeshBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDynamicMeshBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IDynamicMeshBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702B0E1306076100EEC3A0 /* IEventReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventReceiver.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IEventReceiver.h; sourceTree = SOURCE_ROOT; }; - 95702B0F1306076100EEC3A0 /* IFileArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IFileArchive.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IFileArchive.h; sourceTree = SOURCE_ROOT; }; - 95702B101306076100EEC3A0 /* IFileList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IFileList.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IFileList.h; sourceTree = SOURCE_ROOT; }; - 95702B111306076100EEC3A0 /* IFileSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IFileSystem.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IFileSystem.h; sourceTree = SOURCE_ROOT; }; - 95702B121306076100EEC3A0 /* IGeometryCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGeometryCreator.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGeometryCreator.h; sourceTree = SOURCE_ROOT; }; - 95702B131306076100EEC3A0 /* IGPUProgrammingServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGPUProgrammingServices.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGPUProgrammingServices.h; sourceTree = SOURCE_ROOT; }; - 95702B141306076100EEC3A0 /* IGUIButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIButton.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIButton.h; sourceTree = SOURCE_ROOT; }; - 95702B151306076100EEC3A0 /* IGUICheckBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUICheckBox.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUICheckBox.h; sourceTree = SOURCE_ROOT; }; - 95702B161306076100EEC3A0 /* IGUIColorSelectDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIColorSelectDialog.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIColorSelectDialog.h; sourceTree = SOURCE_ROOT; }; - 95702B171306076100EEC3A0 /* IGUIComboBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIComboBox.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIComboBox.h; sourceTree = SOURCE_ROOT; }; - 95702B181306076100EEC3A0 /* IGUIContextMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIContextMenu.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIContextMenu.h; sourceTree = SOURCE_ROOT; }; - 95702B191306076100EEC3A0 /* IGUIEditBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIEditBox.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIEditBox.h; sourceTree = SOURCE_ROOT; }; - 95702B1A1306076100EEC3A0 /* IGUIElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIElement.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIElement.h; sourceTree = SOURCE_ROOT; }; - 95702B1B1306076100EEC3A0 /* IGUIElementFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIElementFactory.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIElementFactory.h; sourceTree = SOURCE_ROOT; }; - 95702B1C1306076100EEC3A0 /* IGUIEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIEnvironment.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIEnvironment.h; sourceTree = SOURCE_ROOT; }; - 95702B1D1306076100EEC3A0 /* IGUIFileOpenDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIFileOpenDialog.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIFileOpenDialog.h; sourceTree = SOURCE_ROOT; }; - 95702B1E1306076100EEC3A0 /* IGUIFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIFont.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIFont.h; sourceTree = SOURCE_ROOT; }; - 95702B1F1306076100EEC3A0 /* IGUIFontBitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIFontBitmap.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIFontBitmap.h; sourceTree = SOURCE_ROOT; }; - 95702B201306076100EEC3A0 /* IGUIImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIImage.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIImage.h; sourceTree = SOURCE_ROOT; }; - 95702B211306076100EEC3A0 /* IGUIImageList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIImageList.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIImageList.h; sourceTree = SOURCE_ROOT; }; - 95702B221306076100EEC3A0 /* IGUIInOutFader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIInOutFader.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIInOutFader.h; sourceTree = SOURCE_ROOT; }; - 95702B231306076100EEC3A0 /* IGUIListBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIListBox.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIListBox.h; sourceTree = SOURCE_ROOT; }; - 95702B241306076100EEC3A0 /* IGUIMeshViewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIMeshViewer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIMeshViewer.h; sourceTree = SOURCE_ROOT; }; - 95702B251306076100EEC3A0 /* IGUIScrollBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIScrollBar.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIScrollBar.h; sourceTree = SOURCE_ROOT; }; - 95702B261306076100EEC3A0 /* IGUISkin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUISkin.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUISkin.h; sourceTree = SOURCE_ROOT; }; - 95702B271306076100EEC3A0 /* IGUISpinBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUISpinBox.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUISpinBox.h; sourceTree = SOURCE_ROOT; }; - 95702B281306076100EEC3A0 /* IGUISpriteBank.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUISpriteBank.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUISpriteBank.h; sourceTree = SOURCE_ROOT; }; - 95702B291306076100EEC3A0 /* IGUIStaticText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIStaticText.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIStaticText.h; sourceTree = SOURCE_ROOT; }; - 95702B2A1306076100EEC3A0 /* IGUITabControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUITabControl.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUITabControl.h; sourceTree = SOURCE_ROOT; }; - 95702B2B1306076100EEC3A0 /* IGUITable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUITable.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUITable.h; sourceTree = SOURCE_ROOT; }; - 95702B2C1306076100EEC3A0 /* IGUIToolbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIToolbar.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIToolbar.h; sourceTree = SOURCE_ROOT; }; - 95702B2D1306076100EEC3A0 /* IGUITreeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUITreeView.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUITreeView.h; sourceTree = SOURCE_ROOT; }; - 95702B2E1306076100EEC3A0 /* IGUIWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IGUIWindow.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IGUIWindow.h; sourceTree = SOURCE_ROOT; }; - 95702B2F1306076100EEC3A0 /* IImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IImage.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IImage.h; sourceTree = SOURCE_ROOT; }; - 95702B301306076100EEC3A0 /* IImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IImageLoader.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IImageLoader.h; sourceTree = SOURCE_ROOT; }; - 95702B311306076100EEC3A0 /* IImageWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IImageWriter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IImageWriter.h; sourceTree = SOURCE_ROOT; }; - 95702B321306076100EEC3A0 /* IIndexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IIndexBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IIndexBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702B331306076100EEC3A0 /* ILightManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ILightManager.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ILightManager.h; sourceTree = SOURCE_ROOT; }; - 95702B341306076100EEC3A0 /* ILightSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ILightSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ILightSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B351306076100EEC3A0 /* ILogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ILogger.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ILogger.h; sourceTree = SOURCE_ROOT; }; - 95702B361306076100EEC3A0 /* IMaterialRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMaterialRenderer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMaterialRenderer.h; sourceTree = SOURCE_ROOT; }; - 95702B371306076100EEC3A0 /* IMaterialRendererServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMaterialRendererServices.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMaterialRendererServices.h; sourceTree = SOURCE_ROOT; }; - 95702B381306076100EEC3A0 /* IMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMesh.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMesh.h; sourceTree = SOURCE_ROOT; }; - 95702B391306076100EEC3A0 /* IMeshBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMeshBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMeshBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702B3A1306076100EEC3A0 /* IMeshCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMeshCache.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMeshCache.h; sourceTree = SOURCE_ROOT; }; - 95702B3B1306076100EEC3A0 /* IMeshLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMeshLoader.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMeshLoader.h; sourceTree = SOURCE_ROOT; }; - 95702B3C1306076100EEC3A0 /* IMeshManipulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMeshManipulator.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMeshManipulator.h; sourceTree = SOURCE_ROOT; }; - 95702B3D1306076100EEC3A0 /* IMeshSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMeshSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMeshSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B3E1306076100EEC3A0 /* IMeshWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMeshWriter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMeshWriter.h; sourceTree = SOURCE_ROOT; }; - 95702B3F1306076100EEC3A0 /* IMetaTriangleSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IMetaTriangleSelector.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IMetaTriangleSelector.h; sourceTree = SOURCE_ROOT; }; - 95702B401306076100EEC3A0 /* IOSOperator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOSOperator.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IOSOperator.h; sourceTree = SOURCE_ROOT; }; - 95702B411306076100EEC3A0 /* IParticleAffector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleAffector.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleAffector.h; sourceTree = SOURCE_ROOT; }; - 95702B421306076100EEC3A0 /* IParticleAnimatedMeshSceneNodeEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleAnimatedMeshSceneNodeEmitter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleAnimatedMeshSceneNodeEmitter.h; sourceTree = SOURCE_ROOT; }; - 95702B431306076100EEC3A0 /* IParticleAttractionAffector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleAttractionAffector.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleAttractionAffector.h; sourceTree = SOURCE_ROOT; }; - 95702B441306076100EEC3A0 /* IParticleBoxEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleBoxEmitter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleBoxEmitter.h; sourceTree = SOURCE_ROOT; }; - 95702B451306076100EEC3A0 /* IParticleCylinderEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleCylinderEmitter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleCylinderEmitter.h; sourceTree = SOURCE_ROOT; }; - 95702B461306076100EEC3A0 /* IParticleEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleEmitter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleEmitter.h; sourceTree = SOURCE_ROOT; }; - 95702B471306076100EEC3A0 /* IParticleFadeOutAffector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleFadeOutAffector.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleFadeOutAffector.h; sourceTree = SOURCE_ROOT; }; - 95702B481306076100EEC3A0 /* IParticleGravityAffector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleGravityAffector.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleGravityAffector.h; sourceTree = SOURCE_ROOT; }; - 95702B491306076100EEC3A0 /* IParticleMeshEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleMeshEmitter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleMeshEmitter.h; sourceTree = SOURCE_ROOT; }; - 95702B4A1306076100EEC3A0 /* IParticleRingEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleRingEmitter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleRingEmitter.h; sourceTree = SOURCE_ROOT; }; - 95702B4B1306076100EEC3A0 /* IParticleRotationAffector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleRotationAffector.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleRotationAffector.h; sourceTree = SOURCE_ROOT; }; - 95702B4C1306076100EEC3A0 /* IParticleSphereEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleSphereEmitter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleSphereEmitter.h; sourceTree = SOURCE_ROOT; }; - 95702B4D1306076100EEC3A0 /* IParticleSystemSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IParticleSystemSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IParticleSystemSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B4E1306076100EEC3A0 /* IQ3LevelMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IQ3LevelMesh.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IQ3LevelMesh.h; sourceTree = SOURCE_ROOT; }; - 95702B4F1306076100EEC3A0 /* IQ3Shader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IQ3Shader.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IQ3Shader.h; sourceTree = SOURCE_ROOT; }; - 95702B501306076100EEC3A0 /* IReadFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IReadFile.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IReadFile.h; sourceTree = SOURCE_ROOT; }; - 95702B511306076100EEC3A0 /* IReferenceCounted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IReferenceCounted.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IReferenceCounted.h; sourceTree = SOURCE_ROOT; }; - 95702B521306076100EEC3A0 /* irrAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrAllocator.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrAllocator.h; sourceTree = SOURCE_ROOT; }; - 95702B531306076100EEC3A0 /* irrArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrArray.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrArray.h; sourceTree = SOURCE_ROOT; }; - 95702B541306076100EEC3A0 /* IrrCompileConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IrrCompileConfig.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IrrCompileConfig.h; sourceTree = SOURCE_ROOT; }; - 95702B551306076100EEC3A0 /* irrlicht.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrlicht.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrlicht.h; sourceTree = SOURCE_ROOT; }; - 95702B561306076100EEC3A0 /* IrrlichtDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IrrlichtDevice.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IrrlichtDevice.h; sourceTree = SOURCE_ROOT; }; - 95702B571306076100EEC3A0 /* irrList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrList.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrList.h; sourceTree = SOURCE_ROOT; }; - 95702B581306076100EEC3A0 /* irrMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrMap.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrMap.h; sourceTree = SOURCE_ROOT; }; - 95702B591306076100EEC3A0 /* irrMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrMath.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrMath.h; sourceTree = SOURCE_ROOT; }; - 95702B5A1306076100EEC3A0 /* irrString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrString.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrString.h; sourceTree = SOURCE_ROOT; }; - 95702B5B1306076100EEC3A0 /* irrTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrTypes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrTypes.h; sourceTree = SOURCE_ROOT; }; - 95702B5C1306076100EEC3A0 /* irrXML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = irrXML.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/irrXML.h; sourceTree = SOURCE_ROOT; }; - 95702B5D1306076100EEC3A0 /* ISceneCollisionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneCollisionManager.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneCollisionManager.h; sourceTree = SOURCE_ROOT; }; - 95702B5E1306076100EEC3A0 /* ISceneManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneManager.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneManager.h; sourceTree = SOURCE_ROOT; }; - 95702B5F1306076100EEC3A0 /* ISceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B601306076100EEC3A0 /* ISceneNodeAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneNodeAnimator.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneNodeAnimator.h; sourceTree = SOURCE_ROOT; }; - 95702B611306076100EEC3A0 /* ISceneNodeAnimatorCameraFPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneNodeAnimatorCameraFPS.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneNodeAnimatorCameraFPS.h; sourceTree = SOURCE_ROOT; }; - 95702B621306076100EEC3A0 /* ISceneNodeAnimatorCameraMaya.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneNodeAnimatorCameraMaya.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneNodeAnimatorCameraMaya.h; sourceTree = SOURCE_ROOT; }; - 95702B631306076100EEC3A0 /* ISceneNodeAnimatorCollisionResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneNodeAnimatorCollisionResponse.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneNodeAnimatorCollisionResponse.h; sourceTree = SOURCE_ROOT; }; - 95702B641306076100EEC3A0 /* ISceneNodeAnimatorFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneNodeAnimatorFactory.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneNodeAnimatorFactory.h; sourceTree = SOURCE_ROOT; }; - 95702B651306076100EEC3A0 /* ISceneNodeFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneNodeFactory.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneNodeFactory.h; sourceTree = SOURCE_ROOT; }; - 95702B661306076100EEC3A0 /* ISceneUserDataSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISceneUserDataSerializer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISceneUserDataSerializer.h; sourceTree = SOURCE_ROOT; }; - 95702B671306076100EEC3A0 /* IShaderConstantSetCallBack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IShaderConstantSetCallBack.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IShaderConstantSetCallBack.h; sourceTree = SOURCE_ROOT; }; - 95702B681306076100EEC3A0 /* IShadowVolumeSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IShadowVolumeSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IShadowVolumeSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B691306076100EEC3A0 /* ISkinnedMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISkinnedMesh.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ISkinnedMesh.h; sourceTree = SOURCE_ROOT; }; - 95702B6A1306076100EEC3A0 /* ITerrainSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITerrainSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ITerrainSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B6B1306076100EEC3A0 /* ITextSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITextSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ITextSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B6C1306076100EEC3A0 /* ITexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITexture.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ITexture.h; sourceTree = SOURCE_ROOT; }; - 95702B6D1306076100EEC3A0 /* ITimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITimer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ITimer.h; sourceTree = SOURCE_ROOT; }; - 95702B6E1306076100EEC3A0 /* ITriangleSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITriangleSelector.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/ITriangleSelector.h; sourceTree = SOURCE_ROOT; }; - 95702B6F1306076100EEC3A0 /* IVertexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IVertexBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IVertexBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702B701306076100EEC3A0 /* IVideoDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IVideoDriver.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IVideoDriver.h; sourceTree = SOURCE_ROOT; }; - 95702B711306076100EEC3A0 /* IVideoModeList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IVideoModeList.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IVideoModeList.h; sourceTree = SOURCE_ROOT; }; - 95702B721306076100EEC3A0 /* IVolumeLightSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IVolumeLightSceneNode.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IVolumeLightSceneNode.h; sourceTree = SOURCE_ROOT; }; - 95702B731306076100EEC3A0 /* IWriteFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IWriteFile.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IWriteFile.h; sourceTree = SOURCE_ROOT; }; - 95702B741306076100EEC3A0 /* IXMLReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IXMLReader.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IXMLReader.h; sourceTree = SOURCE_ROOT; }; - 95702B751306076100EEC3A0 /* IXMLWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IXMLWriter.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/IXMLWriter.h; sourceTree = SOURCE_ROOT; }; - 95702B761306076100EEC3A0 /* Keycodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Keycodes.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/Keycodes.h; sourceTree = SOURCE_ROOT; }; - 95702B771306076100EEC3A0 /* line2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = line2d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/line2d.h; sourceTree = SOURCE_ROOT; }; - 95702B781306076100EEC3A0 /* line3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = line3d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/line3d.h; sourceTree = SOURCE_ROOT; }; - 95702B791306076100EEC3A0 /* matrix4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = matrix4.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/matrix4.h; sourceTree = SOURCE_ROOT; }; - 95702B7A1306076100EEC3A0 /* path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = path.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/path.h; sourceTree = SOURCE_ROOT; }; - 95702B7B1306076100EEC3A0 /* plane3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = plane3d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/plane3d.h; sourceTree = SOURCE_ROOT; }; - 95702B7C1306076100EEC3A0 /* position2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = position2d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/position2d.h; sourceTree = SOURCE_ROOT; }; - 95702B7D1306076100EEC3A0 /* quaternion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = quaternion.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/quaternion.h; sourceTree = SOURCE_ROOT; }; - 95702B7E1306076100EEC3A0 /* rect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rect.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/rect.h; sourceTree = SOURCE_ROOT; }; - 95702B7F1306076100EEC3A0 /* S3DVertex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = S3DVertex.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/S3DVertex.h; sourceTree = SOURCE_ROOT; }; - 95702B801306076100EEC3A0 /* SAnimatedMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SAnimatedMesh.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SAnimatedMesh.h; sourceTree = SOURCE_ROOT; }; - 95702B811306076100EEC3A0 /* SceneParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SceneParameters.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SceneParameters.h; sourceTree = SOURCE_ROOT; }; - 95702B821306076100EEC3A0 /* SColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SColor.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SColor.h; sourceTree = SOURCE_ROOT; }; - 95702B831306076100EEC3A0 /* SExposedVideoData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SExposedVideoData.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SExposedVideoData.h; sourceTree = SOURCE_ROOT; }; - 95702B841306076100EEC3A0 /* SIrrCreationParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SIrrCreationParameters.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SIrrCreationParameters.h; sourceTree = SOURCE_ROOT; }; - 95702B851306076100EEC3A0 /* SKeyMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SKeyMap.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SKeyMap.h; sourceTree = SOURCE_ROOT; }; - 95702B861306076100EEC3A0 /* SLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SLight.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SLight.h; sourceTree = SOURCE_ROOT; }; - 95702B871306076100EEC3A0 /* SMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMaterial.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SMaterial.h; sourceTree = SOURCE_ROOT; }; - 95702B881306076100EEC3A0 /* SMaterialLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMaterialLayer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SMaterialLayer.h; sourceTree = SOURCE_ROOT; }; - 95702B891306076100EEC3A0 /* SMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMesh.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SMesh.h; sourceTree = SOURCE_ROOT; }; - 95702B8A1306076100EEC3A0 /* SMeshBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMeshBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SMeshBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702B8B1306076100EEC3A0 /* SMeshBufferLightMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMeshBufferLightMap.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SMeshBufferLightMap.h; sourceTree = SOURCE_ROOT; }; - 95702B8C1306076100EEC3A0 /* SMeshBufferTangents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SMeshBufferTangents.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SMeshBufferTangents.h; sourceTree = SOURCE_ROOT; }; - 95702B8D1306076100EEC3A0 /* SParticle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SParticle.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SParticle.h; sourceTree = SOURCE_ROOT; }; - 95702B8E1306076100EEC3A0 /* SSharedMeshBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SSharedMeshBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SSharedMeshBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702B8F1306076100EEC3A0 /* SSkinMeshBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SSkinMeshBuffer.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SSkinMeshBuffer.h; sourceTree = SOURCE_ROOT; }; - 95702B901306076100EEC3A0 /* SVertexIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SVertexIndex.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SVertexIndex.h; sourceTree = SOURCE_ROOT; }; - 95702B911306076100EEC3A0 /* SVertexManipulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SVertexManipulator.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SVertexManipulator.h; sourceTree = SOURCE_ROOT; }; - 95702B921306076100EEC3A0 /* SViewFrustum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SViewFrustum.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/SViewFrustum.h; sourceTree = SOURCE_ROOT; }; - 95702B931306076100EEC3A0 /* triangle3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = triangle3d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/triangle3d.h; sourceTree = SOURCE_ROOT; }; - 95702B941306076100EEC3A0 /* vector2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vector2d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/vector2d.h; sourceTree = SOURCE_ROOT; }; - 95702B951306076100EEC3A0 /* vector3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vector3d.h; path = ../../../../../../Library/Frameworks/IrrFramework.framework/Versions/A/Headers/vector3d.h; sourceTree = SOURCE_ROOT; }; - 9574F17A11206881008D202E /* world_status.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = world_status.cpp; path = ../../modes/world_status.cpp; sourceTree = SOURCE_ROOT; }; - 9574F17B11206881008D202E /* world_status.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = world_status.hpp; path = ../../modes/world_status.hpp; sourceTree = SOURCE_ROOT; }; - 9575720F134F5B890037747B /* CGUIEditBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CGUIEditBox.cpp; path = ../../guiengine/widgets/CGUIEditBox.cpp; sourceTree = SOURCE_ROOT; }; - 95757210134F5B890037747B /* CGUIEditBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CGUIEditBox.h; path = ../../guiengine/widgets/CGUIEditBox.h; sourceTree = SOURCE_ROOT; }; - 957817DA142142C500AD07B2 /* referee.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = referee.cpp; path = ../../graphics/referee.cpp; sourceTree = SOURCE_ROOT; }; - 957817DB142142C500AD07B2 /* referee.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = referee.hpp; path = ../../graphics/referee.hpp; sourceTree = SOURCE_ROOT; }; - 957899B613C8E05F007AA5A3 /* profiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = profiler.cpp; path = ../../utils/profiler.cpp; sourceTree = SOURCE_ROOT; }; - 957899B713C8E05F007AA5A3 /* profiler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = profiler.hpp; path = ../../utils/profiler.hpp; sourceTree = SOURCE_ROOT; }; - 957957A014A3CA3900E72497 /* custom_video_settings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = custom_video_settings.cpp; path = ../../states_screens/dialogs/custom_video_settings.cpp; sourceTree = SOURCE_ROOT; }; - 957957A114A3CA3900E72497 /* custom_video_settings.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = custom_video_settings.hpp; path = ../../states_screens/dialogs/custom_video_settings.hpp; sourceTree = SOURCE_ROOT; }; - 957A2D261405E21D00F45B22 /* hit_effect.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = hit_effect.hpp; path = ../../graphics/hit_effect.hpp; sourceTree = SOURCE_ROOT; }; - 957A2D271405E21D00F45B22 /* hit_sfx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = hit_sfx.cpp; path = ../../graphics/hit_sfx.cpp; sourceTree = SOURCE_ROOT; }; - 957A2D281405E21D00F45B22 /* hit_sfx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = hit_sfx.hpp; path = ../../graphics/hit_sfx.hpp; sourceTree = SOURCE_ROOT; }; - 957ED47E1163FF18002AB42C /* ai_base_controller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ai_base_controller.cpp; path = ../../karts/controller/ai_base_controller.cpp; sourceTree = SOURCE_ROOT; }; - 957ED47F1163FF18002AB42C /* ai_base_controller.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ai_base_controller.hpp; path = ../../karts/controller/ai_base_controller.hpp; sourceTree = SOURCE_ROOT; }; - 958330B210122B4A00C5137E /* engine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = engine.cpp; path = ../../guiengine/engine.cpp; sourceTree = SOURCE_ROOT; }; - 958330B310122B4A00C5137E /* engine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = engine.hpp; path = ../../guiengine/engine.hpp; sourceTree = SOURCE_ROOT; }; - 958330B410122B4A00C5137E /* event_handler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = event_handler.cpp; path = ../../guiengine/event_handler.cpp; sourceTree = SOURCE_ROOT; }; - 958330B510122B4A00C5137E /* event_handler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = event_handler.hpp; path = ../../guiengine/event_handler.hpp; sourceTree = SOURCE_ROOT; }; - 958330B610122B4A00C5137E /* modaldialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = modaldialog.cpp; path = ../../guiengine/modaldialog.cpp; sourceTree = SOURCE_ROOT; }; - 958330B710122B4A00C5137E /* modaldialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = modaldialog.hpp; path = ../../guiengine/modaldialog.hpp; sourceTree = SOURCE_ROOT; }; - 958330BA10122B4A00C5137E /* screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = screen.cpp; path = ../../guiengine/screen.cpp; sourceTree = SOURCE_ROOT; }; - 958330BB10122B4A00C5137E /* screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = screen.hpp; path = ../../guiengine/screen.hpp; sourceTree = SOURCE_ROOT; }; - 958330BC10122B4A00C5137E /* screen_loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = screen_loader.cpp; path = ../../guiengine/screen_loader.cpp; sourceTree = SOURCE_ROOT; }; - 958330BD10122B4A00C5137E /* skin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = skin.cpp; path = ../../guiengine/skin.cpp; sourceTree = SOURCE_ROOT; }; - 958330BE10122B4A00C5137E /* skin.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = skin.hpp; path = ../../guiengine/skin.hpp; sourceTree = SOURCE_ROOT; }; - 958330BF10122B4A00C5137E /* widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = widget.cpp; path = ../../guiengine/widget.cpp; sourceTree = SOURCE_ROOT; }; - 958330C010122B4A00C5137E /* widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = widget.hpp; path = ../../guiengine/widget.hpp; sourceTree = SOURCE_ROOT; }; - 958330C210122B4A00C5137E /* credits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = credits.cpp; path = ../../states_screens/credits.cpp; sourceTree = SOURCE_ROOT; }; - 958330C310122B4A00C5137E /* credits.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = credits.hpp; path = ../../states_screens/credits.hpp; sourceTree = SOURCE_ROOT; }; - 958330C410122B4A00C5137E /* kart_selection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_selection.cpp; path = ../../states_screens/kart_selection.cpp; sourceTree = SOURCE_ROOT; }; - 958330C510122B4A00C5137E /* kart_selection.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_selection.hpp; path = ../../states_screens/kart_selection.hpp; sourceTree = SOURCE_ROOT; }; - 958330C810122B4A00C5137E /* race_gui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_gui.cpp; path = ../../states_screens/race_gui.cpp; sourceTree = SOURCE_ROOT; }; - 958330C910122B4A00C5137E /* race_gui.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_gui.hpp; path = ../../states_screens/race_gui.hpp; sourceTree = SOURCE_ROOT; }; - 958330CA10122B4A00C5137E /* state_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = state_manager.cpp; path = ../../states_screens/state_manager.cpp; sourceTree = SOURCE_ROOT; }; - 958330CB10122B4A00C5137E /* state_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = state_manager.hpp; path = ../../states_screens/state_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9583319710123B0200C5137E /* abstract_state_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = abstract_state_manager.hpp; path = ../../guiengine/abstract_state_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9583319810123B0200C5137E /* abstract_state_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = abstract_state_manager.cpp; path = ../../guiengine/abstract_state_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95833237101243ED00C5137E /* enter_player_name_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = enter_player_name_dialog.cpp; path = ../../states_screens/dialogs/enter_player_name_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 95833238101243ED00C5137E /* enter_player_name_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = enter_player_name_dialog.hpp; path = ../../states_screens/dialogs/enter_player_name_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 95833239101243ED00C5137E /* player_info_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = player_info_dialog.cpp; path = ../../states_screens/dialogs/player_info_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 9583323A101243ED00C5137E /* player_info_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = player_info_dialog.hpp; path = ../../states_screens/dialogs/player_info_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 9583323B101243ED00C5137E /* press_a_key_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = press_a_key_dialog.cpp; path = ../../states_screens/dialogs/press_a_key_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 9583323C101243ED00C5137E /* press_a_key_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = press_a_key_dialog.hpp; path = ../../states_screens/dialogs/press_a_key_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 9583323D101243ED00C5137E /* track_info_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = track_info_dialog.cpp; path = ../../states_screens/dialogs/track_info_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 9583323E101243ED00C5137E /* track_info_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = track_info_dialog.hpp; path = ../../states_screens/dialogs/track_info_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 9584448A1330F89100CEA60A /* dictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dictionary.cpp; path = ../../tinygettext/dictionary.cpp; sourceTree = SOURCE_ROOT; }; - 9584448B1330F89100CEA60A /* dictionary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = dictionary.hpp; path = ../../tinygettext/dictionary.hpp; sourceTree = SOURCE_ROOT; }; - 9584448C1330F89100CEA60A /* dictionary_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dictionary_manager.cpp; path = ../../tinygettext/dictionary_manager.cpp; sourceTree = SOURCE_ROOT; }; - 9584448D1330F89100CEA60A /* dictionary_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = dictionary_manager.hpp; path = ../../tinygettext/dictionary_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9584448E1330F89100CEA60A /* file_system.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = file_system.hpp; path = ../../tinygettext/file_system.hpp; sourceTree = SOURCE_ROOT; }; - 9584448F1330F89100CEA60A /* iconv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = iconv.cpp; path = ../../tinygettext/iconv.cpp; sourceTree = SOURCE_ROOT; }; - 958444901330F89100CEA60A /* iconv.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = iconv.hpp; path = ../../tinygettext/iconv.hpp; sourceTree = SOURCE_ROOT; }; - 958444911330F89100CEA60A /* language.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = language.cpp; path = ../../tinygettext/language.cpp; sourceTree = SOURCE_ROOT; }; - 958444921330F89100CEA60A /* language.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = language.hpp; path = ../../tinygettext/language.hpp; sourceTree = SOURCE_ROOT; }; - 958444951330F89100CEA60A /* log_stream.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = log_stream.hpp; path = ../../tinygettext/log_stream.hpp; sourceTree = SOURCE_ROOT; }; - 958444961330F89100CEA60A /* plural_forms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = plural_forms.cpp; path = ../../tinygettext/plural_forms.cpp; sourceTree = SOURCE_ROOT; }; - 958444971330F89100CEA60A /* plural_forms.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = plural_forms.hpp; path = ../../tinygettext/plural_forms.hpp; sourceTree = SOURCE_ROOT; }; - 958444981330F89100CEA60A /* po_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = po_parser.cpp; path = ../../tinygettext/po_parser.cpp; sourceTree = SOURCE_ROOT; }; - 958444991330F89100CEA60A /* po_parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = po_parser.hpp; path = ../../tinygettext/po_parser.hpp; sourceTree = SOURCE_ROOT; }; - 9584449A1330F89100CEA60A /* stk_file_system.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stk_file_system.cpp; path = ../../tinygettext/stk_file_system.cpp; sourceTree = SOURCE_ROOT; }; - 9584449B1330F89100CEA60A /* stk_file_system.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = stk_file_system.hpp; path = ../../tinygettext/stk_file_system.hpp; sourceTree = SOURCE_ROOT; }; - 9584449C1330F89100CEA60A /* tinygettext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tinygettext.cpp; path = ../../tinygettext/tinygettext.cpp; sourceTree = SOURCE_ROOT; }; - 9584449D1330F89100CEA60A /* tinygettext.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = tinygettext.hpp; path = ../../tinygettext/tinygettext.hpp; sourceTree = SOURCE_ROOT; }; - 9584B309137CAC12008565D7 /* news_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = news_manager.cpp; path = ../../addons/news_manager.cpp; sourceTree = SOURCE_ROOT; }; - 9584B30A137CAC12008565D7 /* news_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = news_manager.hpp; path = ../../addons/news_manager.hpp; sourceTree = SOURCE_ROOT; }; - 9584B30B137CAC12008565D7 /* request.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = request.cpp; path = ../../addons/request.cpp; sourceTree = SOURCE_ROOT; }; - 9584B30C137CAC12008565D7 /* request.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = request.hpp; path = ../../addons/request.hpp; sourceTree = SOURCE_ROOT; }; - 9586318011B1EC9F00B8B4AF /* grand_prix_lose.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = grand_prix_lose.cpp; path = ../../states_screens/grand_prix_lose.cpp; sourceTree = SOURCE_ROOT; }; - 9586318111B1EC9F00B8B4AF /* grand_prix_lose.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = grand_prix_lose.hpp; path = ../../states_screens/grand_prix_lose.hpp; sourceTree = SOURCE_ROOT; }; - 9586318211B1EC9F00B8B4AF /* grand_prix_win.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = grand_prix_win.cpp; path = ../../states_screens/grand_prix_win.cpp; sourceTree = SOURCE_ROOT; }; - 9586318311B1EC9F00B8B4AF /* grand_prix_win.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = grand_prix_win.hpp; path = ../../states_screens/grand_prix_win.hpp; sourceTree = SOURCE_ROOT; }; - 958806E713EB675F005F90FE /* track_sector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = track_sector.cpp; path = ../../tracks/track_sector.cpp; sourceTree = SOURCE_ROOT; }; - 958806E813EB675F005F90FE /* track_sector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = track_sector.hpp; path = ../../tracks/track_sector.hpp; sourceTree = SOURCE_ROOT; }; - 958949A8163F52FB00957345 /* inetwork_http.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = inetwork_http.cpp; path = ../../addons/inetwork_http.cpp; sourceTree = SOURCE_ROOT; }; - 958BD76E117F6AE90095B483 /* music_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = music_manager.cpp; path = ../../audio/music_manager.cpp; sourceTree = SOURCE_ROOT; }; - 958BD76F117F6AE90095B483 /* music_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = music_manager.hpp; path = ../../audio/music_manager.hpp; sourceTree = SOURCE_ROOT; }; - 958D8C52104F523000A81934 /* race_paused_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_paused_dialog.hpp; path = ../../states_screens/dialogs/race_paused_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 958D8C53104F523000A81934 /* race_paused_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_paused_dialog.cpp; path = ../../states_screens/dialogs/race_paused_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 958D924013DA48A70097BC82 /* post_processing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = post_processing.cpp; path = ../../graphics/post_processing.cpp; sourceTree = SOURCE_ROOT; }; - 958D924113DA48A70097BC82 /* post_processing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = post_processing.hpp; path = ../../graphics/post_processing.hpp; sourceTree = SOURCE_ROOT; }; - 9592DC6B13021B350039DBC8 /* minimal_race_gui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minimal_race_gui.cpp; path = ../../states_screens/minimal_race_gui.cpp; sourceTree = SOURCE_ROOT; }; - 9592DC6C13021B350039DBC8 /* minimal_race_gui.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = minimal_race_gui.hpp; path = ../../states_screens/minimal_race_gui.hpp; sourceTree = SOURCE_ROOT; }; - 95933669149EC9B10031FD41 /* overworld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = overworld.cpp; path = ../../modes/overworld.cpp; sourceTree = SOURCE_ROOT; }; - 9593366A149EC9B10031FD41 /* overworld.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = overworld.hpp; path = ../../modes/overworld.hpp; sourceTree = SOURCE_ROOT; }; - 9593A1171609386100AB5EEB /* btBulletCollisionCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBulletCollisionCommon.h; path = ../../../lib/bullet/src/btBulletCollisionCommon.h; sourceTree = SOURCE_ROOT; }; - 9593A1181609386100AB5EEB /* btBulletDynamicsCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBulletDynamicsCommon.h; path = ../../../lib/bullet/src/btBulletDynamicsCommon.h; sourceTree = SOURCE_ROOT; }; - 9593A1191609386100AB5EEB /* Bullet-C-Api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Bullet-C-Api.h"; path = "../../../lib/bullet/src/Bullet-C-Api.h"; sourceTree = SOURCE_ROOT; }; - 9593A11C1609386100AB5EEB /* btAxisSweep3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btAxisSweep3.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp; sourceTree = SOURCE_ROOT; }; - 9593A11D1609386100AB5EEB /* btAxisSweep3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btAxisSweep3.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h; sourceTree = SOURCE_ROOT; }; - 9593A11E1609386100AB5EEB /* btBroadphaseInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBroadphaseInterface.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h; sourceTree = SOURCE_ROOT; }; - 9593A11F1609386100AB5EEB /* btBroadphaseProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btBroadphaseProxy.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1201609386100AB5EEB /* btBroadphaseProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBroadphaseProxy.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h; sourceTree = SOURCE_ROOT; }; - 9593A1211609386100AB5EEB /* btCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1221609386100AB5EEB /* btCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1231609386100AB5EEB /* btDbvt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btDbvt.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvt.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1241609386100AB5EEB /* btDbvt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDbvt.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvt.h; sourceTree = SOURCE_ROOT; }; - 9593A1251609386100AB5EEB /* btDbvtBroadphase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btDbvtBroadphase.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1261609386100AB5EEB /* btDbvtBroadphase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDbvtBroadphase.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h; sourceTree = SOURCE_ROOT; }; - 9593A1271609386100AB5EEB /* btDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btDispatcher.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1281609386100AB5EEB /* btDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDispatcher.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h; sourceTree = SOURCE_ROOT; }; - 9593A1291609386100AB5EEB /* btMultiSapBroadphase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btMultiSapBroadphase.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp; sourceTree = SOURCE_ROOT; }; - 9593A12A1609386100AB5EEB /* btMultiSapBroadphase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMultiSapBroadphase.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h; sourceTree = SOURCE_ROOT; }; - 9593A12B1609386100AB5EEB /* btOverlappingPairCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btOverlappingPairCache.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp; sourceTree = SOURCE_ROOT; }; - 9593A12C1609386100AB5EEB /* btOverlappingPairCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btOverlappingPairCache.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h; sourceTree = SOURCE_ROOT; }; - 9593A12D1609386100AB5EEB /* btOverlappingPairCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btOverlappingPairCallback.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h; sourceTree = SOURCE_ROOT; }; - 9593A12E1609386100AB5EEB /* btQuantizedBvh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btQuantizedBvh.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp; sourceTree = SOURCE_ROOT; }; - 9593A12F1609386100AB5EEB /* btQuantizedBvh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btQuantizedBvh.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.h; sourceTree = SOURCE_ROOT; }; - 9593A1301609386100AB5EEB /* btSimpleBroadphase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSimpleBroadphase.cpp; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1311609386100AB5EEB /* btSimpleBroadphase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSimpleBroadphase.h; path = ../../../lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h; sourceTree = SOURCE_ROOT; }; - 9593A1331609386100AB5EEB /* btActivatingCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btActivatingCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1341609386100AB5EEB /* btActivatingCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btActivatingCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1351609386100AB5EEB /* btBox2dBox2dCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btBox2dBox2dCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1361609386100AB5EEB /* btBox2dBox2dCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBox2dBox2dCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1371609386100AB5EEB /* btBoxBoxCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btBoxBoxCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1381609386100AB5EEB /* btBoxBoxCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBoxBoxCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1391609386100AB5EEB /* btBoxBoxDetector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btBoxBoxDetector.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp; sourceTree = SOURCE_ROOT; }; - 9593A13A1609386100AB5EEB /* btBoxBoxDetector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBoxBoxDetector.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.h; sourceTree = SOURCE_ROOT; }; - 9593A13B1609386100AB5EEB /* btCollisionConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionConfiguration.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h; sourceTree = SOURCE_ROOT; }; - 9593A13C1609386100AB5EEB /* btCollisionCreateFunc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionCreateFunc.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h; sourceTree = SOURCE_ROOT; }; - 9593A13D1609386100AB5EEB /* btCollisionDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCollisionDispatcher.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp; sourceTree = SOURCE_ROOT; }; - 9593A13E1609386100AB5EEB /* btCollisionDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionDispatcher.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h; sourceTree = SOURCE_ROOT; }; - 9593A13F1609386100AB5EEB /* btCollisionObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCollisionObject.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1401609386100AB5EEB /* btCollisionObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionObject.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h; sourceTree = SOURCE_ROOT; }; - 9593A1411609386100AB5EEB /* btCollisionWorld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCollisionWorld.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1421609386100AB5EEB /* btCollisionWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionWorld.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h; sourceTree = SOURCE_ROOT; }; - 9593A1431609386100AB5EEB /* btCompoundCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCompoundCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1441609386100AB5EEB /* btCompoundCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCompoundCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1451609386100AB5EEB /* btConvex2dConvex2dAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvex2dConvex2dAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1461609386100AB5EEB /* btConvex2dConvex2dAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvex2dConvex2dAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1471609386100AB5EEB /* btConvexConcaveCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexConcaveCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1481609386100AB5EEB /* btConvexConcaveCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexConcaveCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1491609386100AB5EEB /* btConvexConvexAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexConvexAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A14A1609386100AB5EEB /* btConvexConvexAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexConvexAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A14B1609386100AB5EEB /* btConvexPlaneCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexPlaneCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A14C1609386100AB5EEB /* btConvexPlaneCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexPlaneCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A14D1609386100AB5EEB /* btDefaultCollisionConfiguration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btDefaultCollisionConfiguration.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp; sourceTree = SOURCE_ROOT; }; - 9593A14E1609386100AB5EEB /* btDefaultCollisionConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDefaultCollisionConfiguration.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h; sourceTree = SOURCE_ROOT; }; - 9593A14F1609386100AB5EEB /* btEmptyCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btEmptyCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1501609386100AB5EEB /* btEmptyCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btEmptyCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1511609386100AB5EEB /* btGhostObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGhostObject.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1521609386100AB5EEB /* btGhostObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGhostObject.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.h; sourceTree = SOURCE_ROOT; }; - 9593A1531609386100AB5EEB /* btInternalEdgeUtility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btInternalEdgeUtility.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1541609386100AB5EEB /* btInternalEdgeUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btInternalEdgeUtility.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h; sourceTree = SOURCE_ROOT; }; - 9593A1551609386100AB5EEB /* btManifoldResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btManifoldResult.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1561609386100AB5EEB /* btManifoldResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btManifoldResult.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h; sourceTree = SOURCE_ROOT; }; - 9593A1571609386100AB5EEB /* btSimulationIslandManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSimulationIslandManager.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1581609386100AB5EEB /* btSimulationIslandManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSimulationIslandManager.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h; sourceTree = SOURCE_ROOT; }; - 9593A1591609386100AB5EEB /* btSphereBoxCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSphereBoxCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A15A1609386100AB5EEB /* btSphereBoxCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSphereBoxCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A15B1609386100AB5EEB /* btSphereSphereCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSphereSphereCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A15C1609386100AB5EEB /* btSphereSphereCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSphereSphereCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A15D1609386100AB5EEB /* btSphereTriangleCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSphereTriangleCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A15E1609386100AB5EEB /* btSphereTriangleCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSphereTriangleCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A15F1609386100AB5EEB /* btUnionFind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btUnionFind.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1601609386100AB5EEB /* btUnionFind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btUnionFind.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.h; sourceTree = SOURCE_ROOT; }; - 9593A1611609386100AB5EEB /* SphereTriangleDetector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SphereTriangleDetector.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1621609386100AB5EEB /* SphereTriangleDetector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SphereTriangleDetector.h; path = ../../../lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h; sourceTree = SOURCE_ROOT; }; - 9593A1641609386100AB5EEB /* btBox2dShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btBox2dShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1651609386100AB5EEB /* btBox2dShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBox2dShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1661609386100AB5EEB /* btBoxShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btBoxShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1671609386100AB5EEB /* btBoxShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBoxShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1681609386100AB5EEB /* btBvhTriangleMeshShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btBvhTriangleMeshShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1691609386100AB5EEB /* btBvhTriangleMeshShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBvhTriangleMeshShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h; sourceTree = SOURCE_ROOT; }; - 9593A16A1609386100AB5EEB /* btCapsuleShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCapsuleShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A16B1609386100AB5EEB /* btCapsuleShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCapsuleShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h; sourceTree = SOURCE_ROOT; }; - 9593A16C1609386100AB5EEB /* btCollisionMargin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionMargin.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h; sourceTree = SOURCE_ROOT; }; - 9593A16D1609386100AB5EEB /* btCollisionShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCollisionShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A16E1609386100AB5EEB /* btCollisionShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCollisionShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h; sourceTree = SOURCE_ROOT; }; - 9593A16F1609386100AB5EEB /* btCompoundShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCompoundShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1701609386100AB5EEB /* btCompoundShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCompoundShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1711609386100AB5EEB /* btConcaveShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConcaveShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1721609386100AB5EEB /* btConcaveShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConcaveShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1731609386100AB5EEB /* btConeShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConeShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1741609386100AB5EEB /* btConeShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConeShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1751609386100AB5EEB /* btConvex2dShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvex2dShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvex2dShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1761609386100AB5EEB /* btConvex2dShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvex2dShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvex2dShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1771609386100AB5EEB /* btConvexHullShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexHullShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1781609386100AB5EEB /* btConvexHullShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexHullShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1791609386100AB5EEB /* btConvexInternalShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexInternalShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A17A1609386100AB5EEB /* btConvexInternalShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexInternalShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h; sourceTree = SOURCE_ROOT; }; - 9593A17B1609386100AB5EEB /* btConvexPointCloudShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexPointCloudShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A17C1609386100AB5EEB /* btConvexPointCloudShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexPointCloudShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.h; sourceTree = SOURCE_ROOT; }; - 9593A17D1609386100AB5EEB /* btConvexPolyhedron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexPolyhedron.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp; sourceTree = SOURCE_ROOT; }; - 9593A17E1609386100AB5EEB /* btConvexPolyhedron.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexPolyhedron.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexPolyhedron.h; sourceTree = SOURCE_ROOT; }; - 9593A17F1609386100AB5EEB /* btConvexShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1801609386100AB5EEB /* btConvexShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1811609386100AB5EEB /* btConvexTriangleMeshShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexTriangleMeshShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1821609386100AB5EEB /* btConvexTriangleMeshShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexTriangleMeshShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1831609386100AB5EEB /* btCylinderShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btCylinderShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1841609386100AB5EEB /* btCylinderShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCylinderShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1851609386100AB5EEB /* btEmptyShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btEmptyShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btEmptyShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1861609386100AB5EEB /* btEmptyShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btEmptyShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btEmptyShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1871609386100AB5EEB /* btHeightfieldTerrainShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btHeightfieldTerrainShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1881609386100AB5EEB /* btHeightfieldTerrainShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btHeightfieldTerrainShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1891609386100AB5EEB /* btMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMaterial.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btMaterial.h; sourceTree = SOURCE_ROOT; }; - 9593A18A1609386100AB5EEB /* btMinkowskiSumShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btMinkowskiSumShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A18B1609386100AB5EEB /* btMinkowskiSumShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMinkowskiSumShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h; sourceTree = SOURCE_ROOT; }; - 9593A18C1609386100AB5EEB /* btMultimaterialTriangleMeshShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btMultimaterialTriangleMeshShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A18D1609386100AB5EEB /* btMultimaterialTriangleMeshShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMultimaterialTriangleMeshShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h; sourceTree = SOURCE_ROOT; }; - 9593A18E1609386100AB5EEB /* btMultiSphereShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btMultiSphereShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A18F1609386100AB5EEB /* btMultiSphereShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMultiSphereShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1901609386100AB5EEB /* btOptimizedBvh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btOptimizedBvh.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1911609386100AB5EEB /* btOptimizedBvh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btOptimizedBvh.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h; sourceTree = SOURCE_ROOT; }; - 9593A1921609386100AB5EEB /* btPolyhedralConvexShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btPolyhedralConvexShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1931609386100AB5EEB /* btPolyhedralConvexShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btPolyhedralConvexShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1941609386100AB5EEB /* btScaledBvhTriangleMeshShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btScaledBvhTriangleMeshShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1951609386100AB5EEB /* btScaledBvhTriangleMeshShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btScaledBvhTriangleMeshShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1961609386100AB5EEB /* btShapeHull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btShapeHull.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1971609386100AB5EEB /* btShapeHull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btShapeHull.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.h; sourceTree = SOURCE_ROOT; }; - 9593A1981609386100AB5EEB /* btSphereShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSphereShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1991609386100AB5EEB /* btSphereShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSphereShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btSphereShape.h; sourceTree = SOURCE_ROOT; }; - 9593A19A1609386100AB5EEB /* btStaticPlaneShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btStaticPlaneShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A19B1609386100AB5EEB /* btStaticPlaneShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btStaticPlaneShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h; sourceTree = SOURCE_ROOT; }; - 9593A19C1609386100AB5EEB /* btStridingMeshInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btStridingMeshInterface.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp; sourceTree = SOURCE_ROOT; }; - 9593A19D1609386100AB5EEB /* btStridingMeshInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btStridingMeshInterface.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h; sourceTree = SOURCE_ROOT; }; - 9593A19E1609386100AB5EEB /* btTetrahedronShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTetrahedronShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A19F1609386100AB5EEB /* btTetrahedronShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTetrahedronShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1A01609386100AB5EEB /* btTriangleBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTriangleBuffer.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleBuffer.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1A11609386100AB5EEB /* btTriangleBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleBuffer.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleBuffer.h; sourceTree = SOURCE_ROOT; }; - 9593A1A21609386100AB5EEB /* btTriangleCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTriangleCallback.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1A31609386100AB5EEB /* btTriangleCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleCallback.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h; sourceTree = SOURCE_ROOT; }; - 9593A1A41609386100AB5EEB /* btTriangleIndexVertexArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTriangleIndexVertexArray.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1A51609386100AB5EEB /* btTriangleIndexVertexArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleIndexVertexArray.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h; sourceTree = SOURCE_ROOT; }; - 9593A1A61609386100AB5EEB /* btTriangleIndexVertexMaterialArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTriangleIndexVertexMaterialArray.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1A71609386100AB5EEB /* btTriangleIndexVertexMaterialArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleIndexVertexMaterialArray.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h; sourceTree = SOURCE_ROOT; }; - 9593A1A81609386100AB5EEB /* btTriangleInfoMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleInfoMap.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleInfoMap.h; sourceTree = SOURCE_ROOT; }; - 9593A1A91609386100AB5EEB /* btTriangleMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTriangleMesh.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1AA1609386100AB5EEB /* btTriangleMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleMesh.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h; sourceTree = SOURCE_ROOT; }; - 9593A1AB1609386100AB5EEB /* btTriangleMeshShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTriangleMeshShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1AC1609386100AB5EEB /* btTriangleMeshShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleMeshShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1AD1609386100AB5EEB /* btTriangleShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1AE1609386100AB5EEB /* btUniformScalingShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btUniformScalingShape.cpp; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1AF1609386100AB5EEB /* btUniformScalingShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btUniformScalingShape.h; path = ../../../lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1B11609386100AB5EEB /* btBoxCollision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btBoxCollision.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btBoxCollision.h; sourceTree = SOURCE_ROOT; }; - 9593A1B21609386100AB5EEB /* btClipPolygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btClipPolygon.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btClipPolygon.h; sourceTree = SOURCE_ROOT; }; - 9593A1B31609386100AB5EEB /* btContactProcessing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btContactProcessing.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btContactProcessing.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1B41609386100AB5EEB /* btContactProcessing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btContactProcessing.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btContactProcessing.h; sourceTree = SOURCE_ROOT; }; - 9593A1B51609386100AB5EEB /* btGenericPoolAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGenericPoolAllocator.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGenericPoolAllocator.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1B61609386100AB5EEB /* btGenericPoolAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGenericPoolAllocator.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGenericPoolAllocator.h; sourceTree = SOURCE_ROOT; }; - 9593A1B71609386100AB5EEB /* btGeometryOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGeometryOperations.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGeometryOperations.h; sourceTree = SOURCE_ROOT; }; - 9593A1B81609386100AB5EEB /* btGImpactBvh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGImpactBvh.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactBvh.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1B91609386100AB5EEB /* btGImpactBvh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGImpactBvh.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactBvh.h; sourceTree = SOURCE_ROOT; }; - 9593A1BA1609386100AB5EEB /* btGImpactCollisionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGImpactCollisionAlgorithm.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1BB1609386100AB5EEB /* btGImpactCollisionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGImpactCollisionAlgorithm.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h; sourceTree = SOURCE_ROOT; }; - 9593A1BC1609386100AB5EEB /* btGImpactMassUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGImpactMassUtil.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactMassUtil.h; sourceTree = SOURCE_ROOT; }; - 9593A1BD1609386100AB5EEB /* btGImpactQuantizedBvh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGImpactQuantizedBvh.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1BE1609386100AB5EEB /* btGImpactQuantizedBvh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGImpactQuantizedBvh.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.h; sourceTree = SOURCE_ROOT; }; - 9593A1BF1609386100AB5EEB /* btGImpactShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGImpactShape.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1C01609386100AB5EEB /* btGImpactShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGImpactShape.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h; sourceTree = SOURCE_ROOT; }; - 9593A1C11609386100AB5EEB /* btQuantization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btQuantization.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btQuantization.h; sourceTree = SOURCE_ROOT; }; - 9593A1C21609386100AB5EEB /* btTriangleShapeEx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTriangleShapeEx.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btTriangleShapeEx.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1C31609386100AB5EEB /* btTriangleShapeEx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTriangleShapeEx.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/btTriangleShapeEx.h; sourceTree = SOURCE_ROOT; }; - 9593A1C41609386100AB5EEB /* gim_array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_array.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_array.h; sourceTree = SOURCE_ROOT; }; - 9593A1C51609386100AB5EEB /* gim_basic_geometry_operations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_basic_geometry_operations.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_basic_geometry_operations.h; sourceTree = SOURCE_ROOT; }; - 9593A1C61609386100AB5EEB /* gim_bitset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_bitset.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_bitset.h; sourceTree = SOURCE_ROOT; }; - 9593A1C71609386100AB5EEB /* gim_box_collision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_box_collision.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_box_collision.h; sourceTree = SOURCE_ROOT; }; - 9593A1C81609386100AB5EEB /* gim_box_set.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gim_box_set.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_box_set.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1C91609386100AB5EEB /* gim_box_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_box_set.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_box_set.h; sourceTree = SOURCE_ROOT; }; - 9593A1CA1609386100AB5EEB /* gim_clip_polygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_clip_polygon.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_clip_polygon.h; sourceTree = SOURCE_ROOT; }; - 9593A1CB1609386100AB5EEB /* gim_contact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gim_contact.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_contact.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1CC1609386100AB5EEB /* gim_contact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_contact.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_contact.h; sourceTree = SOURCE_ROOT; }; - 9593A1CD1609386100AB5EEB /* gim_geom_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_geom_types.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_geom_types.h; sourceTree = SOURCE_ROOT; }; - 9593A1CE1609386100AB5EEB /* gim_geometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_geometry.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_geometry.h; sourceTree = SOURCE_ROOT; }; - 9593A1CF1609386100AB5EEB /* gim_hash_table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_hash_table.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_hash_table.h; sourceTree = SOURCE_ROOT; }; - 9593A1D01609386100AB5EEB /* gim_linear_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_linear_math.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_linear_math.h; sourceTree = SOURCE_ROOT; }; - 9593A1D11609386100AB5EEB /* gim_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_math.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_math.h; sourceTree = SOURCE_ROOT; }; - 9593A1D21609386100AB5EEB /* gim_memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gim_memory.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_memory.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1D31609386100AB5EEB /* gim_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_memory.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_memory.h; sourceTree = SOURCE_ROOT; }; - 9593A1D41609386100AB5EEB /* gim_radixsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_radixsort.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_radixsort.h; sourceTree = SOURCE_ROOT; }; - 9593A1D51609386100AB5EEB /* gim_tri_collision.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gim_tri_collision.cpp; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_tri_collision.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1D61609386100AB5EEB /* gim_tri_collision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gim_tri_collision.h; path = ../../../lib/bullet/src/BulletCollision/Gimpact/gim_tri_collision.h; sourceTree = SOURCE_ROOT; }; - 9593A1D81609386100AB5EEB /* btContinuousConvexCollision.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btContinuousConvexCollision.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1D91609386100AB5EEB /* btContinuousConvexCollision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btContinuousConvexCollision.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h; sourceTree = SOURCE_ROOT; }; - 9593A1DA1609386100AB5EEB /* btConvexCast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexCast.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1DB1609386100AB5EEB /* btConvexCast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexCast.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h; sourceTree = SOURCE_ROOT; }; - 9593A1DC1609386100AB5EEB /* btConvexPenetrationDepthSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexPenetrationDepthSolver.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h; sourceTree = SOURCE_ROOT; }; - 9593A1DD1609386100AB5EEB /* btDiscreteCollisionDetectorInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDiscreteCollisionDetectorInterface.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h; sourceTree = SOURCE_ROOT; }; - 9593A1DE1609386100AB5EEB /* btGjkConvexCast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGjkConvexCast.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1DF1609386100AB5EEB /* btGjkConvexCast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGjkConvexCast.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h; sourceTree = SOURCE_ROOT; }; - 9593A1E01609386100AB5EEB /* btGjkEpa2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGjkEpa2.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1E11609386100AB5EEB /* btGjkEpa2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGjkEpa2.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.h; sourceTree = SOURCE_ROOT; }; - 9593A1E21609386100AB5EEB /* btGjkEpaPenetrationDepthSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGjkEpaPenetrationDepthSolver.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1E31609386100AB5EEB /* btGjkEpaPenetrationDepthSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGjkEpaPenetrationDepthSolver.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h; sourceTree = SOURCE_ROOT; }; - 9593A1E41609386100AB5EEB /* btGjkPairDetector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGjkPairDetector.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1E51609386100AB5EEB /* btGjkPairDetector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGjkPairDetector.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h; sourceTree = SOURCE_ROOT; }; - 9593A1E61609386100AB5EEB /* btManifoldPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btManifoldPoint.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h; sourceTree = SOURCE_ROOT; }; - 9593A1E71609386100AB5EEB /* btMinkowskiPenetrationDepthSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btMinkowskiPenetrationDepthSolver.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1E81609386100AB5EEB /* btMinkowskiPenetrationDepthSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMinkowskiPenetrationDepthSolver.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h; sourceTree = SOURCE_ROOT; }; - 9593A1E91609386100AB5EEB /* btPersistentManifold.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btPersistentManifold.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1EA1609386100AB5EEB /* btPersistentManifold.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btPersistentManifold.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h; sourceTree = SOURCE_ROOT; }; - 9593A1EB1609386100AB5EEB /* btPointCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btPointCollector.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPointCollector.h; sourceTree = SOURCE_ROOT; }; - 9593A1EC1609386100AB5EEB /* btPolyhedralContactClipping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btPolyhedralContactClipping.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1ED1609386100AB5EEB /* btPolyhedralContactClipping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btPolyhedralContactClipping.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.h; sourceTree = SOURCE_ROOT; }; - 9593A1EE1609386100AB5EEB /* btRaycastCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btRaycastCallback.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1EF1609386100AB5EEB /* btRaycastCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btRaycastCallback.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h; sourceTree = SOURCE_ROOT; }; - 9593A1F01609386100AB5EEB /* btSimplexSolverInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSimplexSolverInterface.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h; sourceTree = SOURCE_ROOT; }; - 9593A1F11609386100AB5EEB /* btSubSimplexConvexCast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSubSimplexConvexCast.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1F21609386100AB5EEB /* btSubSimplexConvexCast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSubSimplexConvexCast.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h; sourceTree = SOURCE_ROOT; }; - 9593A1F31609386100AB5EEB /* btVoronoiSimplexSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btVoronoiSimplexSolver.cpp; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1F41609386100AB5EEB /* btVoronoiSimplexSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btVoronoiSimplexSolver.h; path = ../../../lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h; sourceTree = SOURCE_ROOT; }; - 9593A1F71609386100AB5EEB /* btCharacterControllerInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btCharacterControllerInterface.h; path = ../../../lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h; sourceTree = SOURCE_ROOT; }; - 9593A1F81609386100AB5EEB /* btKinematicCharacterController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btKinematicCharacterController.cpp; path = ../../../lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1F91609386100AB5EEB /* btKinematicCharacterController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btKinematicCharacterController.h; path = ../../../lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h; sourceTree = SOURCE_ROOT; }; - 9593A1FB1609386100AB5EEB /* btConeTwistConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConeTwistConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1FC1609386100AB5EEB /* btConeTwistConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConeTwistConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A1FD1609386100AB5EEB /* btConstraintSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConstraintSolver.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h; sourceTree = SOURCE_ROOT; }; - 9593A1FE1609386100AB5EEB /* btContactConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btContactConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A1FF1609386100AB5EEB /* btContactConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btContactConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2001609386100AB5EEB /* btContactSolverInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btContactSolverInfo.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h; sourceTree = SOURCE_ROOT; }; - 9593A2011609386100AB5EEB /* btGeneric6DofConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGeneric6DofConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2021609386100AB5EEB /* btGeneric6DofConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGeneric6DofConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2031609386100AB5EEB /* btGeneric6DofSpringConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGeneric6DofSpringConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2041609386100AB5EEB /* btGeneric6DofSpringConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGeneric6DofSpringConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2051609386100AB5EEB /* btHinge2Constraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btHinge2Constraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2061609386100AB5EEB /* btHinge2Constraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btHinge2Constraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2071609386100AB5EEB /* btHingeConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btHingeConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2081609386100AB5EEB /* btHingeConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btHingeConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2091609386100AB5EEB /* btJacobianEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btJacobianEntry.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h; sourceTree = SOURCE_ROOT; }; - 9593A20A1609386100AB5EEB /* btPoint2PointConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btPoint2PointConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A20B1609386100AB5EEB /* btPoint2PointConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btPoint2PointConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A20C1609386100AB5EEB /* btSequentialImpulseConstraintSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSequentialImpulseConstraintSolver.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp; sourceTree = SOURCE_ROOT; }; - 9593A20D1609386100AB5EEB /* btSequentialImpulseConstraintSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSequentialImpulseConstraintSolver.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h; sourceTree = SOURCE_ROOT; }; - 9593A20E1609386100AB5EEB /* btSliderConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSliderConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A20F1609386100AB5EEB /* btSliderConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSliderConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2101609386100AB5EEB /* btSolve2LinearConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSolve2LinearConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2111609386100AB5EEB /* btSolve2LinearConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSolve2LinearConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2121609386100AB5EEB /* btSolverBody.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSolverBody.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h; sourceTree = SOURCE_ROOT; }; - 9593A2131609386100AB5EEB /* btSolverConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSolverConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2141609386100AB5EEB /* btTypedConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btTypedConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2151609386100AB5EEB /* btTypedConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTypedConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2161609386100AB5EEB /* btUniversalConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btUniversalConstraint.cpp; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2171609386100AB5EEB /* btUniversalConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btUniversalConstraint.h; path = ../../../lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h; sourceTree = SOURCE_ROOT; }; - 9593A2191609386100AB5EEB /* btActionInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btActionInterface.h; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btActionInterface.h; sourceTree = SOURCE_ROOT; }; - 9593A21A1609386100AB5EEB /* btContinuousDynamicsWorld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btContinuousDynamicsWorld.cpp; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.cpp; sourceTree = SOURCE_ROOT; }; - 9593A21B1609386100AB5EEB /* btContinuousDynamicsWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btContinuousDynamicsWorld.h; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.h; sourceTree = SOURCE_ROOT; }; - 9593A21C1609386100AB5EEB /* btDiscreteDynamicsWorld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btDiscreteDynamicsWorld.cpp; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp; sourceTree = SOURCE_ROOT; }; - 9593A21D1609386100AB5EEB /* btDiscreteDynamicsWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDiscreteDynamicsWorld.h; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h; sourceTree = SOURCE_ROOT; }; - 9593A21E1609386100AB5EEB /* btDynamicsWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDynamicsWorld.h; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h; sourceTree = SOURCE_ROOT; }; - 9593A21F1609386100AB5EEB /* btRigidBody.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btRigidBody.cpp; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2201609386100AB5EEB /* btRigidBody.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btRigidBody.h; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h; sourceTree = SOURCE_ROOT; }; - 9593A2211609386100AB5EEB /* btSimpleDynamicsWorld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSimpleDynamicsWorld.cpp; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2221609386100AB5EEB /* btSimpleDynamicsWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSimpleDynamicsWorld.h; path = ../../../lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h; sourceTree = SOURCE_ROOT; }; - 9593A2231609386100AB5EEB /* Bullet-C-API.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "Bullet-C-API.cpp"; path = "../../../lib/bullet/src/BulletDynamics/Dynamics/Bullet-C-API.cpp"; sourceTree = SOURCE_ROOT; }; - 9593A2251609386100AB5EEB /* btRaycastVehicle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btRaycastVehicle.cpp; path = ../../../lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2261609386100AB5EEB /* btRaycastVehicle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btRaycastVehicle.h; path = ../../../lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h; sourceTree = SOURCE_ROOT; }; - 9593A2271609386100AB5EEB /* btVehicleRaycaster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btVehicleRaycaster.h; path = ../../../lib/bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h; sourceTree = SOURCE_ROOT; }; - 9593A2281609386100AB5EEB /* btWheelInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btWheelInfo.cpp; path = ../../../lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2291609386100AB5EEB /* btWheelInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btWheelInfo.h; path = ../../../lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h; sourceTree = SOURCE_ROOT; }; - 9593A22B1609386100AB5EEB /* btAabbUtil2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btAabbUtil2.h; path = ../../../lib/bullet/src/LinearMath/btAabbUtil2.h; sourceTree = SOURCE_ROOT; }; - 9593A22C1609386100AB5EEB /* btAlignedAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btAlignedAllocator.cpp; path = ../../../lib/bullet/src/LinearMath/btAlignedAllocator.cpp; sourceTree = SOURCE_ROOT; }; - 9593A22D1609386100AB5EEB /* btAlignedAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btAlignedAllocator.h; path = ../../../lib/bullet/src/LinearMath/btAlignedAllocator.h; sourceTree = SOURCE_ROOT; }; - 9593A22E1609386100AB5EEB /* btAlignedObjectArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btAlignedObjectArray.h; path = ../../../lib/bullet/src/LinearMath/btAlignedObjectArray.h; sourceTree = SOURCE_ROOT; }; - 9593A22F1609386100AB5EEB /* btConvexHull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexHull.cpp; path = ../../../lib/bullet/src/LinearMath/btConvexHull.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2301609386100AB5EEB /* btConvexHull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexHull.h; path = ../../../lib/bullet/src/LinearMath/btConvexHull.h; sourceTree = SOURCE_ROOT; }; - 9593A2311609386100AB5EEB /* btConvexHullComputer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btConvexHullComputer.cpp; path = ../../../lib/bullet/src/LinearMath/btConvexHullComputer.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2321609386100AB5EEB /* btConvexHullComputer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btConvexHullComputer.h; path = ../../../lib/bullet/src/LinearMath/btConvexHullComputer.h; sourceTree = SOURCE_ROOT; }; - 9593A2331609386100AB5EEB /* btDefaultMotionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btDefaultMotionState.h; path = ../../../lib/bullet/src/LinearMath/btDefaultMotionState.h; sourceTree = SOURCE_ROOT; }; - 9593A2341609386100AB5EEB /* btGeometryUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btGeometryUtil.cpp; path = ../../../lib/bullet/src/LinearMath/btGeometryUtil.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2351609386100AB5EEB /* btGeometryUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGeometryUtil.h; path = ../../../lib/bullet/src/LinearMath/btGeometryUtil.h; sourceTree = SOURCE_ROOT; }; - 9593A2361609386100AB5EEB /* btGrahamScan2dConvexHull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btGrahamScan2dConvexHull.h; path = ../../../lib/bullet/src/LinearMath/btGrahamScan2dConvexHull.h; sourceTree = SOURCE_ROOT; }; - 9593A2371609386100AB5EEB /* btHashMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btHashMap.h; path = ../../../lib/bullet/src/LinearMath/btHashMap.h; sourceTree = SOURCE_ROOT; }; - 9593A2381609386100AB5EEB /* btIDebugDraw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btIDebugDraw.h; path = ../../../lib/bullet/src/LinearMath/btIDebugDraw.h; sourceTree = SOURCE_ROOT; }; - 9593A2391609386100AB5EEB /* btList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btList.h; path = ../../../lib/bullet/src/LinearMath/btList.h; sourceTree = SOURCE_ROOT; }; - 9593A23A1609386100AB5EEB /* btMatrix3x3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMatrix3x3.h; path = ../../../lib/bullet/src/LinearMath/btMatrix3x3.h; sourceTree = SOURCE_ROOT; }; - 9593A23B1609386100AB5EEB /* btMinMax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMinMax.h; path = ../../../lib/bullet/src/LinearMath/btMinMax.h; sourceTree = SOURCE_ROOT; }; - 9593A23C1609386100AB5EEB /* btMotionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btMotionState.h; path = ../../../lib/bullet/src/LinearMath/btMotionState.h; sourceTree = SOURCE_ROOT; }; - 9593A23D1609386100AB5EEB /* btPoolAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btPoolAllocator.h; path = ../../../lib/bullet/src/LinearMath/btPoolAllocator.h; sourceTree = SOURCE_ROOT; }; - 9593A23E1609386100AB5EEB /* btQuadWord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btQuadWord.h; path = ../../../lib/bullet/src/LinearMath/btQuadWord.h; sourceTree = SOURCE_ROOT; }; - 9593A23F1609386100AB5EEB /* btQuaternion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btQuaternion.h; path = ../../../lib/bullet/src/LinearMath/btQuaternion.h; sourceTree = SOURCE_ROOT; }; - 9593A2401609386100AB5EEB /* btQuickprof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btQuickprof.cpp; path = ../../../lib/bullet/src/LinearMath/btQuickprof.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2411609386100AB5EEB /* btQuickprof.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btQuickprof.h; path = ../../../lib/bullet/src/LinearMath/btQuickprof.h; sourceTree = SOURCE_ROOT; }; - 9593A2421609386100AB5EEB /* btRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btRandom.h; path = ../../../lib/bullet/src/LinearMath/btRandom.h; sourceTree = SOURCE_ROOT; }; - 9593A2431609386100AB5EEB /* btScalar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btScalar.h; path = ../../../lib/bullet/src/LinearMath/btScalar.h; sourceTree = SOURCE_ROOT; }; - 9593A2441609386100AB5EEB /* btSerializer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btSerializer.cpp; path = ../../../lib/bullet/src/LinearMath/btSerializer.cpp; sourceTree = SOURCE_ROOT; }; - 9593A2451609386100AB5EEB /* btSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btSerializer.h; path = ../../../lib/bullet/src/LinearMath/btSerializer.h; sourceTree = SOURCE_ROOT; }; - 9593A2461609386100AB5EEB /* btStackAlloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btStackAlloc.h; path = ../../../lib/bullet/src/LinearMath/btStackAlloc.h; sourceTree = SOURCE_ROOT; }; - 9593A2471609386100AB5EEB /* btTransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTransform.h; path = ../../../lib/bullet/src/LinearMath/btTransform.h; sourceTree = SOURCE_ROOT; }; - 9593A2481609386100AB5EEB /* btTransformUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btTransformUtil.h; path = ../../../lib/bullet/src/LinearMath/btTransformUtil.h; sourceTree = SOURCE_ROOT; }; - 9593A2491609386100AB5EEB /* btVector3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = btVector3.h; path = ../../../lib/bullet/src/LinearMath/btVector3.h; sourceTree = SOURCE_ROOT; }; - 9593A24A1609386100AB5EEB /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Makefile.am; path = ../../../lib/bullet/src/Makefile.am; sourceTree = SOURCE_ROOT; }; - 9593A2CF1609388C00AB5EEB /* aclocal.m4 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = aclocal.m4; path = ../../../lib/enet/aclocal.m4; sourceTree = SOURCE_ROOT; }; - 9593A2D01609388C00AB5EEB /* callbacks.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = callbacks.c; path = ../../../lib/enet/callbacks.c; sourceTree = SOURCE_ROOT; }; - 9593A2D11609388C00AB5EEB /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = ChangeLog; path = ../../../lib/enet/ChangeLog; sourceTree = SOURCE_ROOT; }; - 9593A2D21609388C00AB5EEB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = ../../../lib/enet/CMakeLists.txt; sourceTree = SOURCE_ROOT; }; - 9593A2D31609388C00AB5EEB /* compress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = compress.c; path = ../../../lib/enet/compress.c; sourceTree = SOURCE_ROOT; }; - 9593A2D41609388C00AB5EEB /* configure */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = configure; path = ../../../lib/enet/configure; sourceTree = SOURCE_ROOT; }; - 9593A2D51609388C00AB5EEB /* configure.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = configure.in; path = ../../../lib/enet/configure.in; sourceTree = SOURCE_ROOT; }; - 9593A2D61609388C00AB5EEB /* depcomp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = depcomp; path = ../../../lib/enet/depcomp; sourceTree = SOURCE_ROOT; }; - 9593A2D71609388C00AB5EEB /* design.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = design.txt; path = ../../../lib/enet/design.txt; sourceTree = SOURCE_ROOT; }; - 9593A2DF1609388C00AB5EEB /* Doxyfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Doxyfile; path = ../../../lib/enet/Doxyfile; sourceTree = SOURCE_ROOT; }; - 9593A2E01609388C00AB5EEB /* enet.dsp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = enet.dsp; path = ../../../lib/enet/enet.dsp; sourceTree = SOURCE_ROOT; }; - 9593A2E11609388C00AB5EEB /* host.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = host.c; path = ../../../lib/enet/host.c; sourceTree = SOURCE_ROOT; }; - 9593A2E41609388C00AB5EEB /* callbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = callbacks.h; path = ../../../lib/enet/include/enet/callbacks.h; sourceTree = SOURCE_ROOT; }; - 9593A2E51609388C00AB5EEB /* enet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = enet.h; path = ../../../lib/enet/include/enet/enet.h; sourceTree = SOURCE_ROOT; }; - 9593A2E61609388C00AB5EEB /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = list.h; path = ../../../lib/enet/include/enet/list.h; sourceTree = SOURCE_ROOT; }; - 9593A2E71609388C00AB5EEB /* protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = protocol.h; path = ../../../lib/enet/include/enet/protocol.h; sourceTree = SOURCE_ROOT; }; - 9593A2E81609388C00AB5EEB /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = time.h; path = ../../../lib/enet/include/enet/time.h; sourceTree = SOURCE_ROOT; }; - 9593A2E91609388C00AB5EEB /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = types.h; path = ../../../lib/enet/include/enet/types.h; sourceTree = SOURCE_ROOT; }; - 9593A2EA1609388C00AB5EEB /* unix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unix.h; path = ../../../lib/enet/include/enet/unix.h; sourceTree = SOURCE_ROOT; }; - 9593A2EB1609388C00AB5EEB /* utility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = utility.h; path = ../../../lib/enet/include/enet/utility.h; sourceTree = SOURCE_ROOT; }; - 9593A2EC1609388C00AB5EEB /* win32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = win32.h; path = ../../../lib/enet/include/enet/win32.h; sourceTree = SOURCE_ROOT; }; - 9593A2ED1609388C00AB5EEB /* install-sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "install-sh"; path = "../../../lib/enet/install-sh"; sourceTree = SOURCE_ROOT; }; - 9593A2EE1609388C00AB5EEB /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../../../lib/enet/LICENSE; sourceTree = SOURCE_ROOT; }; - 9593A2EF1609388C00AB5EEB /* list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = list.c; path = ../../../lib/enet/list.c; sourceTree = SOURCE_ROOT; }; - 9593A2F01609388C00AB5EEB /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Makefile.am; path = ../../../lib/enet/Makefile.am; sourceTree = SOURCE_ROOT; }; - 9593A2F11609388C00AB5EEB /* missing */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = missing; path = ../../../lib/enet/missing; sourceTree = SOURCE_ROOT; }; - 9593A2F21609388C00AB5EEB /* packet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = packet.c; path = ../../../lib/enet/packet.c; sourceTree = SOURCE_ROOT; }; - 9593A2F31609388C00AB5EEB /* peer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = peer.c; path = ../../../lib/enet/peer.c; sourceTree = SOURCE_ROOT; }; - 9593A2F41609388C00AB5EEB /* protocol.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = protocol.c; path = ../../../lib/enet/protocol.c; sourceTree = SOURCE_ROOT; }; - 9593A2F51609388C00AB5EEB /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README; path = ../../../lib/enet/README; sourceTree = SOURCE_ROOT; }; - 9593A2F61609388C00AB5EEB /* tutorial.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = tutorial.txt; path = ../../../lib/enet/tutorial.txt; sourceTree = SOURCE_ROOT; }; - 9593A2F71609388C00AB5EEB /* unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unix.c; path = ../../../lib/enet/unix.c; sourceTree = SOURCE_ROOT; }; - 9593A2F81609388C00AB5EEB /* win32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = win32.c; path = ../../../lib/enet/win32.c; sourceTree = SOURCE_ROOT; }; - 9593A31E16093A7300AB5EEB /* ai_properties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ai_properties.cpp; path = ../../karts/controller/ai_properties.cpp; sourceTree = SOURCE_ROOT; }; - 9593A31F16093A7300AB5EEB /* ai_properties.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ai_properties.hpp; path = ../../karts/controller/ai_properties.hpp; sourceTree = SOURCE_ROOT; }; - 959482CF10EBC0790031BADF /* track_object_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = track_object_manager.cpp; path = ../../tracks/track_object_manager.cpp; sourceTree = SOURCE_ROOT; }; - 959482D010EBC0790031BADF /* track_object_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = track_object_manager.hpp; path = ../../tracks/track_object_manager.hpp; sourceTree = SOURCE_ROOT; }; - 959482D110EBC0790031BADF /* track_object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = track_object.cpp; path = ../../tracks/track_object.cpp; sourceTree = SOURCE_ROOT; }; - 959482D210EBC0790031BADF /* track_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = track_object.hpp; path = ../../tracks/track_object.hpp; sourceTree = SOURCE_ROOT; }; - 9598A5B413CFBA83000B83EA /* hardware_skinning.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = hardware_skinning.cpp; path = ../../graphics/hardware_skinning.cpp; sourceTree = SOURCE_ROOT; }; - 9598A5B513CFBA83000B83EA /* hardware_skinning.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = hardware_skinning.hpp; path = ../../graphics/hardware_skinning.hpp; sourceTree = SOURCE_ROOT; }; - 959DE0C413C297B90068ED78 /* swatter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = swatter.cpp; path = ../../items/swatter.cpp; sourceTree = SOURCE_ROOT; }; - 959DE0C513C297B90068ED78 /* swatter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = swatter.hpp; path = ../../items/swatter.hpp; sourceTree = SOURCE_ROOT; }; - 95A0BA2213E63F6700620EA6 /* kart_with_stats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_with_stats.cpp; path = ../../karts/kart_with_stats.cpp; sourceTree = SOURCE_ROOT; }; - 95A0BA2313E63F6700620EA6 /* kart_with_stats.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_with_stats.hpp; path = ../../karts/kart_with_stats.hpp; sourceTree = SOURCE_ROOT; }; - 95A118290F77EA3100B18B3D /* input.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = input.hpp; path = ../../input/input.hpp; sourceTree = SOURCE_ROOT; }; - 95A1182A0F77EA3100B18B3D /* input_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = input_manager.cpp; path = ../../input/input_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95A1182B0F77EA3100B18B3D /* input_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = input_manager.hpp; path = ../../input/input_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95A1184A0F77FC3900B18B3D /* input_device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = input_device.cpp; path = ../../input/input_device.cpp; sourceTree = SOURCE_ROOT; }; - 95A1184C0F77FC8800B18B3D /* input_device.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = input_device.hpp; path = ../../input/input_device.hpp; sourceTree = SOURCE_ROOT; }; - 95A1187A0F78024E00B18B3D /* device_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = device_manager.cpp; path = ../../input/device_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95A1187C0F78026D00B18B3D /* device_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = device_manager.hpp; path = ../../input/device_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95A1966014FC422D0074B892 /* ghost_kart.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ghost_kart.cpp; path = ../../karts/ghost_kart.cpp; sourceTree = SOURCE_ROOT; }; - 95A1966114FC422D0074B892 /* ghost_kart.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ghost_kart.hpp; path = ../../karts/ghost_kart.hpp; sourceTree = SOURCE_ROOT; }; - 95A197881502E5020074B892 /* replay_base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = replay_base.cpp; path = ../../replay/replay_base.cpp; sourceTree = SOURCE_ROOT; }; - 95A197891502E5020074B892 /* replay_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = replay_base.hpp; path = ../../replay/replay_base.hpp; sourceTree = SOURCE_ROOT; }; - 95A1978A1502E5020074B892 /* replay_play.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = replay_play.cpp; path = ../../replay/replay_play.cpp; sourceTree = SOURCE_ROOT; }; - 95A1978B1502E5020074B892 /* replay_play.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = replay_play.hpp; path = ../../replay/replay_play.hpp; sourceTree = SOURCE_ROOT; }; - 95A1978C1502E5020074B892 /* replay_recorder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = replay_recorder.cpp; path = ../../replay/replay_recorder.cpp; sourceTree = SOURCE_ROOT; }; - 95A1978D1502E5020074B892 /* replay_recorder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = replay_recorder.hpp; path = ../../replay/replay_recorder.hpp; sourceTree = SOURCE_ROOT; }; - 95A197931502E5320074B892 /* skidding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = skidding.cpp; path = ../../karts/skidding.cpp; sourceTree = SOURCE_ROOT; }; - 95A197941502E5320074B892 /* skidding.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = skidding.hpp; path = ../../karts/skidding.hpp; sourceTree = SOURCE_ROOT; }; - 95A5402D1481BD950086FE38 /* inetwork_http.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = inetwork_http.hpp; path = ../../addons/inetwork_http.hpp; sourceTree = SOURCE_ROOT; }; - 95A540411481BEB60086FE38 /* dummy_network_http.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = dummy_network_http.hpp; path = ../../addons/dummy_network_http.hpp; sourceTree = SOURCE_ROOT; }; - 95A540581481C4760086FE38 /* dummy_sfx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = dummy_sfx.hpp; path = ../../audio/dummy_sfx.hpp; sourceTree = SOURCE_ROOT; }; - 95A5405B1481C4DB0086FE38 /* music_dummy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = music_dummy.hpp; path = ../../audio/music_dummy.hpp; sourceTree = SOURCE_ROOT; }; - 95AA4C65148AF2CC0053771D /* lod_node_loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lod_node_loader.cpp; path = ../../tracks/lod_node_loader.cpp; sourceTree = SOURCE_ROOT; }; - 95AA4C66148AF2CC0053771D /* lod_node_loader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = lod_node_loader.hpp; path = ../../tracks/lod_node_loader.hpp; sourceTree = SOURCE_ROOT; }; - 95B0E8A616A4DC060037391C /* log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = log.cpp; path = ../../utils/log.cpp; sourceTree = SOURCE_ROOT; }; - 95B0E8A716A4DC060037391C /* log.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = log.hpp; path = ../../utils/log.hpp; sourceTree = SOURCE_ROOT; }; - 95B0E8AE16A4DC390037391C /* tgt_log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tgt_log.cpp; path = ../../tinygettext/tgt_log.cpp; sourceTree = SOURCE_ROOT; }; - 95B0E8AF16A4DC390037391C /* tgt_log.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = tgt_log.hpp; path = ../../tinygettext/tgt_log.hpp; sourceTree = SOURCE_ROOT; }; - 95B0E8B316A4DC9E0037391C /* easter_egg_hunt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = easter_egg_hunt.cpp; path = ../../modes/easter_egg_hunt.cpp; sourceTree = SOURCE_ROOT; }; - 95B0E8B416A4DC9E0037391C /* easter_egg_hunt.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = easter_egg_hunt.hpp; path = ../../modes/easter_egg_hunt.hpp; sourceTree = SOURCE_ROOT; }; - 95B0E8B516A4DC9E0037391C /* tutorial_world.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tutorial_world.cpp; path = ../../modes/tutorial_world.cpp; sourceTree = SOURCE_ROOT; }; - 95B0E8B616A4DC9E0037391C /* tutorial_world.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = tutorial_world.hpp; path = ../../modes/tutorial_world.hpp; sourceTree = SOURCE_ROOT; }; - 95B5CD12102DE08F00EF2001 /* device_config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = device_config.hpp; path = ../../config/device_config.hpp; sourceTree = SOURCE_ROOT; }; - 95B5CD13102DE08F00EF2001 /* device_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = device_config.cpp; path = ../../config/device_config.cpp; sourceTree = SOURCE_ROOT; }; - 95BF1E66127513A100F78AE7 /* max_speed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = max_speed.cpp; path = ../../karts/max_speed.cpp; sourceTree = SOURCE_ROOT; }; - 95BF1E67127513A100F78AE7 /* max_speed.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = max_speed.hpp; path = ../../karts/max_speed.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AC270F296540000D3E5D /* music.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = music.hpp; path = ../../audio/music.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AC280F296540000D3E5D /* music_information.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = music_information.cpp; path = ../../audio/music_information.cpp; sourceTree = SOURCE_ROOT; }; - 95C2AC290F296540000D3E5D /* music_information.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = music_information.hpp; path = ../../audio/music_information.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AC2A0F296540000D3E5D /* music_ogg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = music_ogg.cpp; path = ../../audio/music_ogg.cpp; sourceTree = SOURCE_ROOT; }; - 95C2AC2B0F296540000D3E5D /* music_ogg.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = music_ogg.hpp; path = ../../audio/music_ogg.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AC2C0F296540000D3E5D /* sfx_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sfx_base.hpp; path = ../../audio/sfx_base.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AC2D0F296540000D3E5D /* sfx_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sfx_manager.cpp; path = ../../audio/sfx_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2AC2E0F296540000D3E5D /* sfx_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sfx_manager.hpp; path = ../../audio/sfx_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AC2F0F296540000D3E5D /* sfx_openal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sfx_openal.cpp; path = ../../audio/sfx_openal.cpp; sourceTree = SOURCE_ROOT; }; - 95C2AC300F296540000D3E5D /* sfx_openal.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sfx_openal.hpp; path = ../../audio/sfx_openal.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AE260F296541000D3E5D /* challenge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = challenge.cpp; path = ../../challenges/challenge.cpp; sourceTree = SOURCE_ROOT; }; - 95C2AE270F296541000D3E5D /* challenge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = challenge.hpp; path = ../../challenges/challenge.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AE280F296541000D3E5D /* challenge_data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = challenge_data.cpp; path = ../../challenges/challenge_data.cpp; sourceTree = SOURCE_ROOT; }; - 95C2AE290F296541000D3E5D /* challenge_data.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = challenge_data.hpp; path = ../../challenges/challenge_data.hpp; sourceTree = SOURCE_ROOT; }; - 95C2AE2A0F296541000D3E5D /* unlock_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unlock_manager.cpp; path = ../../challenges/unlock_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2AE2B0F296541000D3E5D /* unlock_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = unlock_manager.hpp; path = ../../challenges/unlock_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B0F60F296545000D3E5D /* attachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = attachment.cpp; path = ../../items/attachment.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B0F70F296545000D3E5D /* attachment.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = attachment.hpp; path = ../../items/attachment.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B0F80F296545000D3E5D /* attachment_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = attachment_manager.cpp; path = ../../items/attachment_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B0F90F296545000D3E5D /* attachment_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = attachment_manager.hpp; path = ../../items/attachment_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B0FA0F296545000D3E5D /* bowling.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bowling.cpp; path = ../../items/bowling.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B0FB0F296545000D3E5D /* bowling.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = bowling.hpp; path = ../../items/bowling.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B0FE0F296545000D3E5D /* cake.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cake.cpp; path = ../../items/cake.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B0FF0F296545000D3E5D /* cake.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = cake.hpp; path = ../../items/cake.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1000F296545000D3E5D /* flyable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = flyable.cpp; path = ../../items/flyable.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1010F296545000D3E5D /* flyable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = flyable.hpp; path = ../../items/flyable.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1020F296545000D3E5D /* item.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = item.cpp; path = ../../items/item.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1030F296545000D3E5D /* item.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = item.hpp; path = ../../items/item.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1040F296545000D3E5D /* item_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = item_manager.cpp; path = ../../items/item_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1050F296545000D3E5D /* item_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = item_manager.hpp; path = ../../items/item_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1060F296545000D3E5D /* plunger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = plunger.cpp; path = ../../items/plunger.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1070F296545000D3E5D /* plunger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = plunger.hpp; path = ../../items/plunger.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1080F296545000D3E5D /* powerup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = powerup.cpp; path = ../../items/powerup.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1090F296545000D3E5D /* powerup.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = powerup.hpp; path = ../../items/powerup.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B10A0F296545000D3E5D /* powerup_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = powerup_manager.cpp; path = ../../items/powerup_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B10B0F296545000D3E5D /* powerup_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = powerup_manager.hpp; path = ../../items/powerup_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B10C0F296545000D3E5D /* projectile_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = projectile_manager.cpp; path = ../../items/projectile_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B10D0F296545000D3E5D /* projectile_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = projectile_manager.hpp; path = ../../items/projectile_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B10E0F296545000D3E5D /* rubber_band.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rubber_band.cpp; path = ../../items/rubber_band.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B10F0F296545000D3E5D /* rubber_band.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rubber_band.hpp; path = ../../items/rubber_band.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1180F296545000D3E5D /* kart.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart.cpp; path = ../../karts/kart.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1190F296545000D3E5D /* kart.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart.hpp; path = ../../karts/kart.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B11B0F296545000D3E5D /* kart_model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_model.cpp; path = ../../karts/kart_model.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B11C0F296545000D3E5D /* kart_model.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_model.hpp; path = ../../karts/kart_model.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B11D0F296545000D3E5D /* kart_properties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_properties.cpp; path = ../../karts/kart_properties.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B11E0F296545000D3E5D /* kart_properties.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_properties.hpp; path = ../../karts/kart_properties.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B11F0F296545000D3E5D /* kart_properties_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_properties_manager.cpp; path = ../../karts/kart_properties_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1200F296545000D3E5D /* kart_properties_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_properties_manager.hpp; path = ../../karts/kart_properties_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1210F296545000D3E5D /* moveable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moveable.cpp; path = ../../karts/moveable.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1220F296545000D3E5D /* moveable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = moveable.hpp; path = ../../karts/moveable.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1360F296545000D3E5D /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = ../../main.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1380F296545000D3E5D /* main_loop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main_loop.cpp; path = ../../main_loop.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1390F296545000D3E5D /* main_loop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = main_loop.hpp; path = ../../main_loop.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B14A0F296545000D3E5D /* follow_the_leader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = follow_the_leader.cpp; path = ../../modes/follow_the_leader.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B14B0F296545000D3E5D /* follow_the_leader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = follow_the_leader.hpp; path = ../../modes/follow_the_leader.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B14C0F296545000D3E5D /* linear_world.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = linear_world.cpp; path = ../../modes/linear_world.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B14D0F296545000D3E5D /* linear_world.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = linear_world.hpp; path = ../../modes/linear_world.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B14E0F296545000D3E5D /* standard_race.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = standard_race.cpp; path = ../../modes/standard_race.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B14F0F296545000D3E5D /* standard_race.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = standard_race.hpp; path = ../../modes/standard_race.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1500F296545000D3E5D /* three_strikes_battle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = three_strikes_battle.cpp; path = ../../modes/three_strikes_battle.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1510F296545000D3E5D /* three_strikes_battle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = three_strikes_battle.hpp; path = ../../modes/three_strikes_battle.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1520F296545000D3E5D /* world.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = world.cpp; path = ../../modes/world.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1530F296545000D3E5D /* world.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = world.hpp; path = ../../modes/world.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B15A0F296545000D3E5D /* character_confirm_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = character_confirm_message.hpp; path = ../../network/character_confirm_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B15B0F296545000D3E5D /* character_info_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = character_info_message.hpp; path = ../../network/character_info_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B15C0F296545000D3E5D /* character_selected_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = character_selected_message.hpp; path = ../../network/character_selected_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B15D0F296545000D3E5D /* connect_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = connect_message.cpp; path = ../../network/connect_message.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B15E0F296545000D3E5D /* connect_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = connect_message.hpp; path = ../../network/connect_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B15F0F296545000D3E5D /* flyable_info.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = flyable_info.hpp; path = ../../network/flyable_info.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1600F296545000D3E5D /* item_info.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = item_info.hpp; path = ../../network/item_info.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1610F296545000D3E5D /* kart_control_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_control_message.cpp; path = ../../network/kart_control_message.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1620F296545000D3E5D /* kart_control_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_control_message.hpp; path = ../../network/kart_control_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1630F296545000D3E5D /* kart_update_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = kart_update_message.cpp; path = ../../network/kart_update_message.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1640F296545000D3E5D /* kart_update_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_update_message.hpp; path = ../../network/kart_update_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1650F296545000D3E5D /* message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = message.cpp; path = ../../network/message.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1660F296545000D3E5D /* message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = message.hpp; path = ../../network/message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1670F296545000D3E5D /* network_kart.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = network_kart.cpp; path = ../../network/network_kart.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1680F296545000D3E5D /* network_kart.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = network_kart.hpp; path = ../../network/network_kart.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1690F296545000D3E5D /* network_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = network_manager.cpp; path = ../../network/network_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B16A0F296545000D3E5D /* network_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = network_manager.hpp; path = ../../network/network_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B16B0F296545000D3E5D /* num_players_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = num_players_message.hpp; path = ../../network/num_players_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B16C0F296545000D3E5D /* race_info_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_info_message.cpp; path = ../../network/race_info_message.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B16D0F296545000D3E5D /* race_info_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_info_message.hpp; path = ../../network/race_info_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B16E0F296545000D3E5D /* race_result_ack_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_result_ack_message.hpp; path = ../../network/race_result_ack_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B16F0F296545000D3E5D /* race_result_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_result_message.cpp; path = ../../network/race_result_message.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1700F296545000D3E5D /* race_result_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_result_message.hpp; path = ../../network/race_result_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1710F296545000D3E5D /* race_start_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_start_message.hpp; path = ../../network/race_start_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1720F296545000D3E5D /* race_state.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_state.cpp; path = ../../network/race_state.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1730F296545000D3E5D /* race_state.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_state.hpp; path = ../../network/race_state.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1740F296545000D3E5D /* remote_kart_info.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = remote_kart_info.hpp; path = ../../network/remote_kart_info.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1750F296545000D3E5D /* world_loaded_message.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = world_loaded_message.hpp; path = ../../network/world_loaded_message.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1800F296545000D3E5D /* btKart.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btKart.cpp; path = ../../physics/btKart.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1810F296545000D3E5D /* btKart.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = btKart.hpp; path = ../../physics/btKart.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1820F296545000D3E5D /* btUprightConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = btUprightConstraint.cpp; path = ../../physics/btUprightConstraint.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1830F296545000D3E5D /* btUprightConstraint.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = btUprightConstraint.hpp; path = ../../physics/btUprightConstraint.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1840F296545000D3E5D /* kart_motion_state.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = kart_motion_state.hpp; path = ../../physics/kart_motion_state.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1870F296545000D3E5D /* physics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = physics.cpp; path = ../../physics/physics.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1880F296545000D3E5D /* physics.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = physics.hpp; path = ../../physics/physics.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1CF0F296545000D3E5D /* track.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = track.cpp; path = ../../tracks/track.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1D00F296545000D3E5D /* track.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = track.hpp; path = ../../tracks/track.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1D10F296545000D3E5D /* track_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = track_manager.cpp; path = ../../tracks/track_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1D20F296545000D3E5D /* track_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = track_manager.hpp; path = ../../tracks/track_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1E00F296546000D3E5D /* constants.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = constants.hpp; path = ../../utils/constants.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1E20F296546000D3E5D /* random_generator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = random_generator.cpp; path = ../../utils/random_generator.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1E30F296546000D3E5D /* random_generator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = random_generator.hpp; path = ../../utils/random_generator.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1E60F296546000D3E5D /* string_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_utils.cpp; path = ../../utils/string_utils.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1E70F296546000D3E5D /* string_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = string_utils.hpp; path = ../../utils/string_utils.hpp; sourceTree = SOURCE_ROOT; }; - 95C2B1E80F296546000D3E5D /* vec3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = vec3.cpp; path = ../../utils/vec3.cpp; sourceTree = SOURCE_ROOT; }; - 95C2B1E90F296546000D3E5D /* vec3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = vec3.hpp; path = ../../utils/vec3.hpp; sourceTree = SOURCE_ROOT; }; - 95C631A3142AC79500416D47 /* leak_check.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = leak_check.cpp; path = ../../utils/leak_check.cpp; sourceTree = SOURCE_ROOT; }; - 95C631B3142AC95500416D47 /* leak_check.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = leak_check.hpp; path = ../../utils/leak_check.hpp; sourceTree = SOURCE_ROOT; }; - 95C65D760F532F7D00BE7BA7 /* xml_node.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = xml_node.cpp; path = ../../io/xml_node.cpp; sourceTree = SOURCE_ROOT; }; - 95C65D770F532F7D00BE7BA7 /* xml_node.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = xml_node.hpp; path = ../../io/xml_node.hpp; sourceTree = SOURCE_ROOT; }; - 95C77D611069589B0080838E /* ambient_light_sphere.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ambient_light_sphere.hpp; path = ../../tracks/ambient_light_sphere.hpp; sourceTree = SOURCE_ROOT; }; - 95C77D621069589B0080838E /* ambient_light_sphere.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ambient_light_sphere.cpp; path = ../../tracks/ambient_light_sphere.cpp; sourceTree = SOURCE_ROOT; }; - 95C77D64106958A50080838E /* check_line.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = check_line.hpp; path = ../../tracks/check_line.hpp; sourceTree = SOURCE_ROOT; }; - 95C77D65106958A50080838E /* check_line.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check_line.cpp; path = ../../tracks/check_line.cpp; sourceTree = SOURCE_ROOT; }; - 95C77D6D106958E10080838E /* check_sphere.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check_sphere.cpp; path = ../../tracks/check_sphere.cpp; sourceTree = SOURCE_ROOT; }; - 95C77D6E106958E10080838E /* check_sphere.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = check_sphere.hpp; path = ../../tracks/check_sphere.hpp; sourceTree = SOURCE_ROOT; }; - 95C87F2D133A34E400D86528 /* time.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = time.hpp; path = ../../utils/time.hpp; sourceTree = SOURCE_ROOT; }; - 95C87F2F133A34EE00D86528 /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checked.h; path = ../../utils/utf8/checked.h; sourceTree = SOURCE_ROOT; }; - 95C87F30133A34EE00D86528 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = core.h; path = ../../utils/utf8/core.h; sourceTree = SOURCE_ROOT; }; - 95C87F31133A34EE00D86528 /* unchecked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unchecked.h; path = ../../utils/utf8/unchecked.h; sourceTree = SOURCE_ROOT; }; - 95C87F32133A34EE00D86528 /* utf8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = utf8.h; path = ../../utils/utf8.h; sourceTree = SOURCE_ROOT; }; - 95C9C97010E93456005A418D /* stars.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stars.cpp; path = ../../graphics/stars.cpp; sourceTree = SOURCE_ROOT; }; - 95C9C97110E93456005A418D /* stars.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = stars.hpp; path = ../../graphics/stars.hpp; sourceTree = SOURCE_ROOT; }; - 95CA59F60F82FCB7003323DB /* physical_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = physical_object.hpp; path = ../../physics/physical_object.hpp; sourceTree = SOURCE_ROOT; }; - 95CA59F70F82FCB7003323DB /* physical_object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = physical_object.cpp; path = ../../physics/physical_object.cpp; sourceTree = SOURCE_ROOT; }; - 95CB476A0FF30EF400413BAE /* bezier_curve.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = bezier_curve.hpp; path = ../../tracks/bezier_curve.hpp; sourceTree = SOURCE_ROOT; }; - 95CB476B0FF30EF400413BAE /* bezier_curve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bezier_curve.cpp; path = ../../tracks/bezier_curve.cpp; sourceTree = SOURCE_ROOT; }; - 95D2343D1078227A00625256 /* feature_unlocked.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = feature_unlocked.hpp; path = ../../states_screens/feature_unlocked.hpp; sourceTree = SOURCE_ROOT; }; - 95D2343E1078227A00625256 /* feature_unlocked.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = feature_unlocked.cpp; path = ../../states_screens/feature_unlocked.cpp; sourceTree = SOURCE_ROOT; }; - 95D2C42E13D6605600E84032 /* rubber_ball.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rubber_ball.cpp; path = ../../items/rubber_ball.cpp; sourceTree = SOURCE_ROOT; }; - 95D2C42F13D6605600E84032 /* rubber_ball.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rubber_ball.hpp; path = ../../items/rubber_ball.hpp; sourceTree = SOURCE_ROOT; }; - 95D69A7215226E4700D598D8 /* abstract_kart.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = abstract_kart.cpp; path = ../../karts/abstract_kart.cpp; sourceTree = SOURCE_ROOT; }; - 95D69A7315226E4700D598D8 /* abstract_kart.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = abstract_kart.hpp; path = ../../karts/abstract_kart.hpp; sourceTree = SOURCE_ROOT; }; - 95D71F6416BF380900C0F691 /* classic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = classic.c; path = ../../../lib/wiiuse/classic.c; sourceTree = SOURCE_ROOT; }; - 95D71F6516BF380900C0F691 /* classic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = classic.h; path = ../../../lib/wiiuse/classic.h; sourceTree = SOURCE_ROOT; }; - 95D71F6616BF380900C0F691 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = ../../../lib/wiiuse/CMakeLists.txt; sourceTree = SOURCE_ROOT; }; - 95D71F6716BF380900C0F691 /* definitions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = definitions.h; path = ../../../lib/wiiuse/definitions.h; sourceTree = SOURCE_ROOT; }; - 95D71F6816BF380900C0F691 /* definitions_os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = definitions_os.h; path = ../../../lib/wiiuse/definitions_os.h; sourceTree = SOURCE_ROOT; }; - 95D71F6916BF380900C0F691 /* dynamics.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dynamics.c; path = ../../../lib/wiiuse/dynamics.c; sourceTree = SOURCE_ROOT; }; - 95D71F6A16BF380900C0F691 /* dynamics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dynamics.h; path = ../../../lib/wiiuse/dynamics.h; sourceTree = SOURCE_ROOT; }; - 95D71F6B16BF380900C0F691 /* events.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = events.c; path = ../../../lib/wiiuse/events.c; sourceTree = SOURCE_ROOT; }; - 95D71F6C16BF380900C0F691 /* events.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = events.h; path = ../../../lib/wiiuse/events.h; sourceTree = SOURCE_ROOT; }; - 95D71F6D16BF380900C0F691 /* guitar_hero_3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = guitar_hero_3.c; path = ../../../lib/wiiuse/guitar_hero_3.c; sourceTree = SOURCE_ROOT; }; - 95D71F6E16BF380900C0F691 /* guitar_hero_3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = guitar_hero_3.h; path = ../../../lib/wiiuse/guitar_hero_3.h; sourceTree = SOURCE_ROOT; }; - 95D71F6F16BF380900C0F691 /* io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = io.c; path = ../../../lib/wiiuse/io.c; sourceTree = SOURCE_ROOT; }; - 95D71F7016BF380900C0F691 /* io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = io.h; path = ../../../lib/wiiuse/io.h; sourceTree = SOURCE_ROOT; }; - 95D71F7216BF380900C0F691 /* io_win.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = io_win.c; path = ../../../lib/wiiuse/io_win.c; sourceTree = SOURCE_ROOT; }; - 95D71F7316BF380900C0F691 /* ir.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ir.c; path = ../../../lib/wiiuse/ir.c; sourceTree = SOURCE_ROOT; }; - 95D71F7416BF380900C0F691 /* ir.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ir.h; path = ../../../lib/wiiuse/ir.h; sourceTree = SOURCE_ROOT; }; - 95D71F7516BF380900C0F691 /* motion_plus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = motion_plus.c; path = ../../../lib/wiiuse/motion_plus.c; sourceTree = SOURCE_ROOT; }; - 95D71F7616BF380900C0F691 /* motion_plus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = motion_plus.h; path = ../../../lib/wiiuse/motion_plus.h; sourceTree = SOURCE_ROOT; }; - 95D71F7716BF380900C0F691 /* nunchuk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nunchuk.c; path = ../../../lib/wiiuse/nunchuk.c; sourceTree = SOURCE_ROOT; }; - 95D71F7816BF380900C0F691 /* nunchuk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nunchuk.h; path = ../../../lib/wiiuse/nunchuk.h; sourceTree = SOURCE_ROOT; }; - 95D71F7916BF380900C0F691 /* os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = os.h; path = ../../../lib/wiiuse/os.h; sourceTree = SOURCE_ROOT; }; - 95D71F7B16BF380900C0F691 /* os_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = os_mac.h; path = ../../../lib/wiiuse/os_mac/os_mac.h; sourceTree = SOURCE_ROOT; }; - 95D71F7C16BF380900C0F691 /* os_mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = os_mac.m; path = ../../../lib/wiiuse/os_mac/os_mac.m; sourceTree = SOURCE_ROOT; }; - 95D71F7D16BF380900C0F691 /* os_mac_find.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = os_mac_find.m; path = ../../../lib/wiiuse/os_mac/os_mac_find.m; sourceTree = SOURCE_ROOT; }; - 95D71F7E16BF380900C0F691 /* os_mac_interface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = os_mac_interface.m; path = ../../../lib/wiiuse/os_mac/os_mac_interface.m; sourceTree = SOURCE_ROOT; }; - 95D71F7F16BF380900C0F691 /* os_nix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = os_nix.c; path = ../../../lib/wiiuse/os_nix.c; sourceTree = SOURCE_ROOT; }; - 95D71F8016BF380900C0F691 /* os_win.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = os_win.c; path = ../../../lib/wiiuse/os_win.c; sourceTree = SOURCE_ROOT; }; - 95D71F8116BF380900C0F691 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README; path = ../../../lib/wiiuse/README; sourceTree = SOURCE_ROOT; }; - 95D71F8216BF380900C0F691 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = util.c; path = ../../../lib/wiiuse/util.c; sourceTree = SOURCE_ROOT; }; - 95D71F8316BF380900C0F691 /* wiiboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wiiboard.c; path = ../../../lib/wiiuse/wiiboard.c; sourceTree = SOURCE_ROOT; }; - 95D71F8416BF380900C0F691 /* wiiboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wiiboard.h; path = ../../../lib/wiiuse/wiiboard.h; sourceTree = SOURCE_ROOT; }; - 95D71F8616BF380900C0F691 /* wiiuse.vcproj */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = wiiuse.vcproj; path = ../../../lib/wiiuse/wiiuse/wiiuse.vcproj; sourceTree = SOURCE_ROOT; }; - 95D71F8716BF380900C0F691 /* wiiuse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wiiuse.c; path = ../../../lib/wiiuse/wiiuse.c; sourceTree = SOURCE_ROOT; }; - 95D71F8816BF380900C0F691 /* wiiuse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wiiuse.h; path = ../../../lib/wiiuse/wiiuse.h; sourceTree = SOURCE_ROOT; }; - 95D71F8916BF380900C0F691 /* wiiuse_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wiiuse_internal.h; path = ../../../lib/wiiuse/wiiuse_internal.h; sourceTree = SOURCE_ROOT; }; - 95D71F8A16BF380900C0F691 /* wiiuse_msvcstdint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wiiuse_msvcstdint.h; path = ../../../lib/wiiuse/wiiuse_msvcstdint.h; sourceTree = SOURCE_ROOT; }; - 95D71FA816BF3A4F00C0F691 /* IOBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetooth.framework; path = /System/Library/Frameworks/IOBluetooth.framework; sourceTree = ""; }; - 95D71FB016BF3A9E00C0F691 /* wiimote_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = wiimote_manager.cpp; path = ../../input/wiimote_manager.cpp; sourceTree = SOURCE_ROOT; }; - 95D71FB116BF3A9E00C0F691 /* wiimote_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = wiimote_manager.hpp; path = ../../input/wiimote_manager.hpp; sourceTree = SOURCE_ROOT; }; - 95D71FB516BF3C7800C0F691 /* tutorial_message_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tutorial_message_dialog.cpp; path = ../../states_screens/dialogs/tutorial_message_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 95D71FB616BF3C7800C0F691 /* tutorial_message_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = tutorial_message_dialog.hpp; path = ../../states_screens/dialogs/tutorial_message_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 95D950CD0FE473CA002E10AD /* player.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = player.hpp; path = ../../config/player.hpp; sourceTree = SOURCE_ROOT; }; - 95D950CE0FE473CA002E10AD /* stk_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stk_config.cpp; path = ../../config/stk_config.cpp; sourceTree = SOURCE_ROOT; }; - 95D950CF0FE473CA002E10AD /* stk_config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = stk_config.hpp; path = ../../config/stk_config.hpp; sourceTree = SOURCE_ROOT; }; - 95D950D00FE473CA002E10AD /* user_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = user_config.cpp; path = ../../config/user_config.cpp; sourceTree = SOURCE_ROOT; }; - 95D950D10FE473CA002E10AD /* user_config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = user_config.hpp; path = ../../config/user_config.hpp; sourceTree = SOURCE_ROOT; }; - 95DFC5001106933B00A043A9 /* slip_stream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = slip_stream.cpp; path = ../../graphics/slip_stream.cpp; sourceTree = SOURCE_ROOT; }; - 95DFC5011106933B00A043A9 /* slip_stream.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = slip_stream.hpp; path = ../../graphics/slip_stream.hpp; sourceTree = SOURCE_ROOT; }; - 95E1FCDD130369EB004D83CC /* per_camera_node.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = per_camera_node.cpp; path = ../../graphics/per_camera_node.cpp; sourceTree = SOURCE_ROOT; }; - 95E1FCDE130369EB004D83CC /* per_camera_node.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = per_camera_node.hpp; path = ../../graphics/per_camera_node.hpp; sourceTree = SOURCE_ROOT; }; - 95E1FCE0130369F4004D83CC /* rain.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rain.hpp; path = ../../graphics/rain.hpp; sourceTree = SOURCE_ROOT; }; - 95E246BC111A2959000C965D /* confirm_resolution_dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = confirm_resolution_dialog.cpp; path = ../../states_screens/dialogs/confirm_resolution_dialog.cpp; sourceTree = SOURCE_ROOT; }; - 95E246BD111A2959000C965D /* confirm_resolution_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = confirm_resolution_dialog.hpp; path = ../../states_screens/dialogs/confirm_resolution_dialog.hpp; sourceTree = SOURCE_ROOT; }; - 95E5C316148C17E500AD3FCC /* story_mode_lobby.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = story_mode_lobby.cpp; path = ../../states_screens/story_mode_lobby.cpp; sourceTree = SOURCE_ROOT; }; - 95E5C317148C17E500AD3FCC /* story_mode_lobby.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = story_mode_lobby.hpp; path = ../../states_screens/story_mode_lobby.hpp; sourceTree = SOURCE_ROOT; }; - 95E5C333148C19F500AD3FCC /* story_mode_new.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = story_mode_new.cpp; path = ../../states_screens/dialogs/story_mode_new.cpp; sourceTree = SOURCE_ROOT; }; - 95E5C334148C19F500AD3FCC /* story_mode_new.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = story_mode_new.hpp; path = ../../states_screens/dialogs/story_mode_new.hpp; sourceTree = SOURCE_ROOT; }; - 95E5C3C0148C418100AD3FCC /* game_slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = game_slot.cpp; path = ../../challenges/game_slot.cpp; sourceTree = SOURCE_ROOT; }; - 95E5C3C1148C418100AD3FCC /* game_slot.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = game_slot.hpp; path = ../../challenges/game_slot.hpp; sourceTree = SOURCE_ROOT; }; - 95E6A0BE11DCF37800AE088A /* addons_loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = addons_loading.cpp; path = ../../states_screens/dialogs/addons_loading.cpp; sourceTree = SOURCE_ROOT; }; - 95E6A0BF11DCF37800AE088A /* addons_loading.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = addons_loading.hpp; path = ../../states_screens/dialogs/addons_loading.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0EC10124C5000D47C5F /* button_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = button_widget.cpp; path = ../../guiengine/widgets/button_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0ED10124C5000D47C5F /* button_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = button_widget.hpp; path = ../../guiengine/widgets/button_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0EE10124C5000D47C5F /* check_box_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check_box_widget.cpp; path = ../../guiengine/widgets/check_box_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0EF10124C5000D47C5F /* check_box_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = check_box_widget.hpp; path = ../../guiengine/widgets/check_box_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F010124C5000D47C5F /* icon_button_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = icon_button_widget.cpp; path = ../../guiengine/widgets/icon_button_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F110124C5000D47C5F /* icon_button_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = icon_button_widget.hpp; path = ../../guiengine/widgets/icon_button_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F210124C5000D47C5F /* label_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = label_widget.cpp; path = ../../guiengine/widgets/label_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F310124C5000D47C5F /* label_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = label_widget.hpp; path = ../../guiengine/widgets/label_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F410124C5000D47C5F /* list_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = list_widget.cpp; path = ../../guiengine/widgets/list_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F510124C5000D47C5F /* list_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = list_widget.hpp; path = ../../guiengine/widgets/list_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F610124C5000D47C5F /* model_view_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = model_view_widget.cpp; path = ../../guiengine/widgets/model_view_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0F710124C5000D47C5F /* model_view_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = model_view_widget.hpp; path = ../../guiengine/widgets/model_view_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0FA10124C5000D47C5F /* ribbon_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ribbon_widget.cpp; path = ../../guiengine/widgets/ribbon_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0FB10124C5000D47C5F /* ribbon_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ribbon_widget.hpp; path = ../../guiengine/widgets/ribbon_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0FC10124C5000D47C5F /* spinner_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = spinner_widget.cpp; path = ../../guiengine/widgets/spinner_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0FD10124C5000D47C5F /* spinner_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = spinner_widget.hpp; path = ../../guiengine/widgets/spinner_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA0FE10124C5000D47C5F /* text_box_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = text_box_widget.cpp; path = ../../guiengine/widgets/text_box_widget.cpp; sourceTree = SOURCE_ROOT; }; - 95ECA0FF10124C5000D47C5F /* text_box_widget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = text_box_widget.hpp; path = ../../guiengine/widgets/text_box_widget.hpp; sourceTree = SOURCE_ROOT; }; - 95ECA13C10124E6900D47C5F /* widgets.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = widgets.hpp; path = ../../guiengine/widgets.hpp; sourceTree = SOURCE_ROOT; }; - 95EF178C14AFBC91005FFEEB /* race_gui_overworld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_gui_overworld.cpp; path = ../../states_screens/race_gui_overworld.cpp; sourceTree = SOURCE_ROOT; }; - 95EF178D14AFBC91005FFEEB /* race_gui_overworld.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_gui_overworld.hpp; path = ../../states_screens/race_gui_overworld.hpp; sourceTree = SOURCE_ROOT; }; - 95EF1E081506D6CF0041AC79 /* skidding_properties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = skidding_properties.cpp; path = ../../karts/skidding_properties.cpp; sourceTree = SOURCE_ROOT; }; - 95EF1E091506D6CF0041AC79 /* skidding_properties.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = skidding_properties.hpp; path = ../../karts/skidding_properties.hpp; sourceTree = SOURCE_ROOT; }; - 95F423120E26E3DC00692113 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; - 95F4231E0E26E44800692113 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - 95F423400E26E65B00692113 /* GLUT.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLUT.framework; path = /System/Library/Frameworks/GLUT.framework; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 9551C81F0FC1B6FF00DB481B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 9551CBDA0FC1BB9200DB481B /* AGL.framework in Frameworks */, - 9551CBD10FC1BB7600DB481B /* IOKit.framework in Frameworks */, - 9551CBD20FC1BB7600DB481B /* QuickTime.framework in Frameworks */, - 9551CBD30FC1BB7600DB481B /* Carbon.framework in Frameworks */, - 9551CBD40FC1BB7600DB481B /* AudioUnit.framework in Frameworks */, - 9551CBD50FC1BB7600DB481B /* GLUT.framework in Frameworks */, - 9551CBD60FC1BB7600DB481B /* Cocoa.framework in Frameworks */, - 9551CBD70FC1BB7600DB481B /* OpenGL.framework in Frameworks */, - 9551C9F10FC1B7EE00DB481B /* Ogg.framework in Frameworks */, - 9551C9F20FC1B7EE00DB481B /* OpenAL.framework in Frameworks */, - 9551C9F30FC1B7EE00DB481B /* Vorbis.framework in Frameworks */, - 954E486A11B19C4100B1DF63 /* fribidi.framework in Frameworks */, - 9538E2E912C2682B00172896 /* IrrFramework.framework in Frameworks */, - 95D71FA916BF3A4F00C0F691 /* IOBluetooth.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* STK_XCode */ = { - isa = PBXGroup; - children = ( - 9593A10C1609384700AB5EEB /* Lib */, - 9551C9F80FC1B87600DB481B /* Config.xcconfig */, - 95702AE41306076100EEC3A0 /* Headers */, - 95C2ABA60F29653F000D3E5D /* src */, - 9513B40E0F0EDE80005D29F6 /* Frameworks */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - 9551CA100FC1BB6400DB481B /* SuperTuxKart-Info.plist */, - 9507E9B60FC1CCE900BD2B92 /* stk.icns */, - 95D71FA816BF3A4F00C0F691 /* IOBluetooth.framework */, - ); - name = STK_XCode; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 9551C8210FC1B6FF00DB481B /* SuperTuxKart.app */, - ); - name = Products; - sourceTree = ""; - }; - 9513B40E0F0EDE80005D29F6 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 9538E2E812C2682B00172896 /* IrrFramework.framework */, - 954E486911B19C4100B1DF63 /* fribidi.framework */, - 9551CBD90FC1BB9200DB481B /* AGL.framework */, - 9551C7F90FC1B63C00DB481B /* Ogg.framework */, - 9551C7FA0FC1B63C00DB481B /* OpenAL.framework */, - 9551C7FB0FC1B63C00DB481B /* Vorbis.framework */, - 950557640F6968BE0056E88C /* IOKit.framework */, - 9505575F0F6968A50056E88C /* QuickTime.framework */, - 950557570F6968860056E88C /* Carbon.framework */, - 9505574E0F69684D0056E88C /* AudioUnit.framework */, - 95F423400E26E65B00692113 /* GLUT.framework */, - 95F4231E0E26E44800692113 /* Cocoa.framework */, - 95F423120E26E3DC00692113 /* OpenGL.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 95263DDF0FD7471900CF5F92 /* race */ = { - isa = PBXGroup; - children = ( - 95263DE00FD7471900CF5F92 /* grand_prix_data.cpp */, - 95263DE10FD7471900CF5F92 /* grand_prix_data.hpp */, - 95263DE20FD7471900CF5F92 /* grand_prix_manager.cpp */, - 95263DE30FD7471900CF5F92 /* grand_prix_manager.hpp */, - 95263DE40FD7471900CF5F92 /* highscore_manager.cpp */, - 95263DE50FD7471900CF5F92 /* highscore_manager.hpp */, - 95263DE60FD7471900CF5F92 /* highscores.cpp */, - 95263DE70FD7471900CF5F92 /* highscores.hpp */, - 95263DE80FD7471900CF5F92 /* history.cpp */, - 95263DE90FD7471900CF5F92 /* history.hpp */, - 95263DEA0FD7471900CF5F92 /* race_manager.cpp */, - 95263DEB0FD7471900CF5F92 /* race_manager.hpp */, - ); - name = race; - path = ../../race; - sourceTree = SOURCE_ROOT; - }; - 952A152C103F66D600B1895D /* graphics */ = { - isa = PBXGroup; - children = ( - 9529980F15B207740028301A /* show_curve.cpp */, - 9529981015B207740028301A /* show_curve.hpp */, - 9525B71211C851D30094BD96 /* CBatchingMesh.cpp */, - 9525B71311C851D30094BD96 /* CBatchingMesh.hpp */, - 952A152D103F66D600B1895D /* camera.cpp */, - 952A152E103F66D600B1895D /* camera.hpp */, - 952A152F103F66D600B1895D /* explosion.cpp */, - 952A1530103F66D600B1895D /* explosion.hpp */, - 9598A5B413CFBA83000B83EA /* hardware_skinning.cpp */, - 9598A5B513CFBA83000B83EA /* hardware_skinning.hpp */, - 957A2D261405E21D00F45B22 /* hit_effect.hpp */, - 957A2D271405E21D00F45B22 /* hit_sfx.cpp */, - 957A2D281405E21D00F45B22 /* hit_sfx.hpp */, - 952A1531103F66D600B1895D /* irr_driver.cpp */, - 952A1532103F66D600B1895D /* irr_driver.hpp */, - 95376CAD1320784100C842A4 /* lod_node.cpp */, - 95376CAE1320784100C842A4 /* lod_node.hpp */, - 952A1533103F66D600B1895D /* material.cpp */, - 952A1534103F66D600B1895D /* material.hpp */, - 952A1535103F66D600B1895D /* material_manager.cpp */, - 952A1536103F66D600B1895D /* material_manager.hpp */, - 952A1537103F66D600B1895D /* mesh_tools.cpp */, - 952A1538103F66D600B1895D /* mesh_tools.hpp */, - 952A1539103F66D600B1895D /* moving_texture.cpp */, - 952A153A103F66D600B1895D /* moving_texture.hpp */, - 9542FC7512D3BDB000C00366 /* particle_emitter.cpp */, - 9542FC7612D3BDB000C00366 /* particle_emitter.hpp */, - 9542FD4A12D3E0D700C00366 /* particle_kind.cpp */, - 9542FD4B12D3E0D700C00366 /* particle_kind.hpp */, - 9528C71412D69494006E9167 /* particle_kind_manager.cpp */, - 9528C71512D69494006E9167 /* particle_kind_manager.hpp */, - 95E1FCDD130369EB004D83CC /* per_camera_node.cpp */, - 95E1FCDE130369EB004D83CC /* per_camera_node.hpp */, - 958D924013DA48A70097BC82 /* post_processing.cpp */, - 958D924113DA48A70097BC82 /* post_processing.hpp */, - 9559DE7E12FF777600350DE8 /* rain.cpp */, - 95E1FCE0130369F4004D83CC /* rain.hpp */, - 957817DA142142C500AD07B2 /* referee.cpp */, - 957817DB142142C500AD07B2 /* referee.hpp */, - 952A153D103F66D600B1895D /* shadow.cpp */, - 952A153E103F66D600B1895D /* shadow.hpp */, - 952A153F103F66D600B1895D /* skid_marks.cpp */, - 952A1540103F66D600B1895D /* skid_marks.hpp */, - 95DFC5001106933B00A043A9 /* slip_stream.cpp */, - 95DFC5011106933B00A043A9 /* slip_stream.hpp */, - 95C9C97010E93456005A418D /* stars.cpp */, - 95C9C97110E93456005A418D /* stars.hpp */, - ); - name = graphics; - path = ../../graphics; - sourceTree = SOURCE_ROOT; - }; - 954E4C240FF98B6E0047FE3E /* animations */ = { - isa = PBXGroup; - children = ( - 954E4C250FF98B6E0047FE3E /* animation_base.cpp */, - 954E4C260FF98B6E0047FE3E /* animation_base.hpp */, - 954E4C290FF98B6E0047FE3E /* billboard_animation.cpp */, - 954E4C2A0FF98B6E0047FE3E /* billboard_animation.hpp */, - 951BC65C0FFAF290006B5FF1 /* ipo.cpp */, - 951BC65D0FFAF290006B5FF1 /* ipo.hpp */, - 954E4C2B0FF98B6E0047FE3E /* three_d_animation.cpp */, - 954E4C2C0FF98B6E0047FE3E /* three_d_animation.hpp */, - ); - name = animations; - path = ../../animations; - sourceTree = SOURCE_ROOT; - }; - 9551B27311DC0D6A002DD140 /* addons */ = { - isa = PBXGroup; - children = ( - 958949A8163F52FB00957345 /* inetwork_http.cpp */, - 9538A55E12CD094200CE3220 /* addon.cpp */, - 9538A55F12CD094200CE3220 /* addon.hpp */, - 9538E2B512C25D6800172896 /* addons_manager.cpp */, - 9538E2B612C25D6800172896 /* addons_manager.hpp */, - 95A540411481BEB60086FE38 /* dummy_network_http.hpp */, - 95A5402D1481BD950086FE38 /* inetwork_http.hpp */, - 9538E2B712C25D6800172896 /* network_http.cpp */, - 9538E2B812C25D6800172896 /* network_http.hpp */, - 9584B309137CAC12008565D7 /* news_manager.cpp */, - 9584B30A137CAC12008565D7 /* news_manager.hpp */, - 9584B30B137CAC12008565D7 /* request.cpp */, - 9584B30C137CAC12008565D7 /* request.hpp */, - 9551B27811DC0D6A002DD140 /* zip.cpp */, - 9551B27911DC0D6A002DD140 /* zip.hpp */, - ); - name = addons; - path = ../../addons; - sourceTree = SOURCE_ROOT; - }; - 956C6EC71128D3FB004336C8 /* controller */ = { - isa = PBXGroup; - children = ( - 9593A31E16093A7300AB5EEB /* ai_properties.cpp */, - 9593A31F16093A7300AB5EEB /* ai_properties.hpp */, - 952997EA15B206890028301A /* skidding_ai.cpp */, - 952997EB15B206890028301A /* skidding_ai.hpp */, - 957ED47E1163FF18002AB42C /* ai_base_controller.cpp */, - 957ED47F1163FF18002AB42C /* ai_base_controller.hpp */, - 956C6EC81128D3FB004336C8 /* controller.cpp */, - 956C6EC91128D3FB004336C8 /* controller.hpp */, - 956C6ECC1128D3FB004336C8 /* end_controller.cpp */, - 956C6ECD1128D3FB004336C8 /* end_controller.hpp */, - 956C6ECE1128D3FB004336C8 /* kart_control.hpp */, - 956C6ED11128D3FB004336C8 /* player_controller.cpp */, - 956C6ED21128D3FB004336C8 /* player_controller.hpp */, - ); - name = controller; - path = ../../karts/controller; - sourceTree = SOURCE_ROOT; - }; - 95702AE41306076100EEC3A0 /* Headers */ = { - isa = PBXGroup; - children = ( - 95702AE51306076100EEC3A0 /* aabbox3d.h */, - 95702AE61306076100EEC3A0 /* CDynamicMeshBuffer.h */, - 95702AE71306076100EEC3A0 /* CIndexBuffer.h */, - 95702AE81306076100EEC3A0 /* CMeshBuffer.h */, - 95702AE91306076100EEC3A0 /* coreutil.h */, - 95702AEA1306076100EEC3A0 /* CVertexBuffer.h */, - 95702AEB1306076100EEC3A0 /* dimension2d.h */, - 95702AEC1306076100EEC3A0 /* driverChoice.h */, - 95702AED1306076100EEC3A0 /* EAttributes.h */, - 95702AEE1306076100EEC3A0 /* ECullingTypes.h */, - 95702AEF1306076100EEC3A0 /* EDebugSceneTypes.h */, - 95702AF01306076100EEC3A0 /* EDeviceTypes.h */, - 95702AF11306076100EEC3A0 /* EDriverFeatures.h */, - 95702AF21306076100EEC3A0 /* EDriverTypes.h */, - 95702AF31306076100EEC3A0 /* EGUIAlignment.h */, - 95702AF41306076100EEC3A0 /* EGUIElementTypes.h */, - 95702AF51306076100EEC3A0 /* EHardwareBufferFlags.h */, - 95702AF61306076100EEC3A0 /* EMaterialFlags.h */, - 95702AF71306076100EEC3A0 /* EMaterialTypes.h */, - 95702AF81306076100EEC3A0 /* EMeshWriterEnums.h */, - 95702AF91306076100EEC3A0 /* EMessageBoxFlags.h */, - 95702AFA1306076100EEC3A0 /* EPrimitiveTypes.h */, - 95702AFB1306076100EEC3A0 /* ESceneNodeAnimatorTypes.h */, - 95702AFC1306076100EEC3A0 /* ESceneNodeTypes.h */, - 95702AFD1306076100EEC3A0 /* EShaderTypes.h */, - 95702AFE1306076100EEC3A0 /* ETerrainElements.h */, - 95702AFF1306076100EEC3A0 /* fast_atof.h */, - 95702B001306076100EEC3A0 /* heapsort.h */, - 95702B011306076100EEC3A0 /* IAnimatedMesh.h */, - 95702B021306076100EEC3A0 /* IAnimatedMeshMD2.h */, - 95702B031306076100EEC3A0 /* IAnimatedMeshMD3.h */, - 95702B041306076100EEC3A0 /* IAnimatedMeshSceneNode.h */, - 95702B051306076100EEC3A0 /* IAttributeExchangingObject.h */, - 95702B061306076100EEC3A0 /* IAttributes.h */, - 95702B071306076100EEC3A0 /* IBillboardSceneNode.h */, - 95702B081306076100EEC3A0 /* IBillboardTextSceneNode.h */, - 95702B091306076100EEC3A0 /* IBoneSceneNode.h */, - 95702B0A1306076100EEC3A0 /* ICameraSceneNode.h */, - 95702B0B1306076100EEC3A0 /* ICursorControl.h */, - 95702B0C1306076100EEC3A0 /* IDummyTransformationSceneNode.h */, - 95702B0D1306076100EEC3A0 /* IDynamicMeshBuffer.h */, - 95702B0E1306076100EEC3A0 /* IEventReceiver.h */, - 95702B0F1306076100EEC3A0 /* IFileArchive.h */, - 95702B101306076100EEC3A0 /* IFileList.h */, - 95702B111306076100EEC3A0 /* IFileSystem.h */, - 95702B121306076100EEC3A0 /* IGeometryCreator.h */, - 95702B131306076100EEC3A0 /* IGPUProgrammingServices.h */, - 95702B141306076100EEC3A0 /* IGUIButton.h */, - 95702B151306076100EEC3A0 /* IGUICheckBox.h */, - 95702B161306076100EEC3A0 /* IGUIColorSelectDialog.h */, - 95702B171306076100EEC3A0 /* IGUIComboBox.h */, - 95702B181306076100EEC3A0 /* IGUIContextMenu.h */, - 95702B191306076100EEC3A0 /* IGUIEditBox.h */, - 95702B1A1306076100EEC3A0 /* IGUIElement.h */, - 95702B1B1306076100EEC3A0 /* IGUIElementFactory.h */, - 95702B1C1306076100EEC3A0 /* IGUIEnvironment.h */, - 95702B1D1306076100EEC3A0 /* IGUIFileOpenDialog.h */, - 95702B1E1306076100EEC3A0 /* IGUIFont.h */, - 95702B1F1306076100EEC3A0 /* IGUIFontBitmap.h */, - 95702B201306076100EEC3A0 /* IGUIImage.h */, - 95702B211306076100EEC3A0 /* IGUIImageList.h */, - 95702B221306076100EEC3A0 /* IGUIInOutFader.h */, - 95702B231306076100EEC3A0 /* IGUIListBox.h */, - 95702B241306076100EEC3A0 /* IGUIMeshViewer.h */, - 95702B251306076100EEC3A0 /* IGUIScrollBar.h */, - 95702B261306076100EEC3A0 /* IGUISkin.h */, - 95702B271306076100EEC3A0 /* IGUISpinBox.h */, - 95702B281306076100EEC3A0 /* IGUISpriteBank.h */, - 95702B291306076100EEC3A0 /* IGUIStaticText.h */, - 95702B2A1306076100EEC3A0 /* IGUITabControl.h */, - 95702B2B1306076100EEC3A0 /* IGUITable.h */, - 95702B2C1306076100EEC3A0 /* IGUIToolbar.h */, - 95702B2D1306076100EEC3A0 /* IGUITreeView.h */, - 95702B2E1306076100EEC3A0 /* IGUIWindow.h */, - 95702B2F1306076100EEC3A0 /* IImage.h */, - 95702B301306076100EEC3A0 /* IImageLoader.h */, - 95702B311306076100EEC3A0 /* IImageWriter.h */, - 95702B321306076100EEC3A0 /* IIndexBuffer.h */, - 95702B331306076100EEC3A0 /* ILightManager.h */, - 95702B341306076100EEC3A0 /* ILightSceneNode.h */, - 95702B351306076100EEC3A0 /* ILogger.h */, - 95702B361306076100EEC3A0 /* IMaterialRenderer.h */, - 95702B371306076100EEC3A0 /* IMaterialRendererServices.h */, - 95702B381306076100EEC3A0 /* IMesh.h */, - 95702B391306076100EEC3A0 /* IMeshBuffer.h */, - 95702B3A1306076100EEC3A0 /* IMeshCache.h */, - 95702B3B1306076100EEC3A0 /* IMeshLoader.h */, - 95702B3C1306076100EEC3A0 /* IMeshManipulator.h */, - 95702B3D1306076100EEC3A0 /* IMeshSceneNode.h */, - 95702B3E1306076100EEC3A0 /* IMeshWriter.h */, - 95702B3F1306076100EEC3A0 /* IMetaTriangleSelector.h */, - 95702B401306076100EEC3A0 /* IOSOperator.h */, - 95702B411306076100EEC3A0 /* IParticleAffector.h */, - 95702B421306076100EEC3A0 /* IParticleAnimatedMeshSceneNodeEmitter.h */, - 95702B431306076100EEC3A0 /* IParticleAttractionAffector.h */, - 95702B441306076100EEC3A0 /* IParticleBoxEmitter.h */, - 95702B451306076100EEC3A0 /* IParticleCylinderEmitter.h */, - 95702B461306076100EEC3A0 /* IParticleEmitter.h */, - 95702B471306076100EEC3A0 /* IParticleFadeOutAffector.h */, - 95702B481306076100EEC3A0 /* IParticleGravityAffector.h */, - 95702B491306076100EEC3A0 /* IParticleMeshEmitter.h */, - 95702B4A1306076100EEC3A0 /* IParticleRingEmitter.h */, - 95702B4B1306076100EEC3A0 /* IParticleRotationAffector.h */, - 95702B4C1306076100EEC3A0 /* IParticleSphereEmitter.h */, - 95702B4D1306076100EEC3A0 /* IParticleSystemSceneNode.h */, - 95702B4E1306076100EEC3A0 /* IQ3LevelMesh.h */, - 95702B4F1306076100EEC3A0 /* IQ3Shader.h */, - 95702B501306076100EEC3A0 /* IReadFile.h */, - 95702B511306076100EEC3A0 /* IReferenceCounted.h */, - 95702B521306076100EEC3A0 /* irrAllocator.h */, - 95702B531306076100EEC3A0 /* irrArray.h */, - 95702B541306076100EEC3A0 /* IrrCompileConfig.h */, - 95702B551306076100EEC3A0 /* irrlicht.h */, - 95702B561306076100EEC3A0 /* IrrlichtDevice.h */, - 95702B571306076100EEC3A0 /* irrList.h */, - 95702B581306076100EEC3A0 /* irrMap.h */, - 95702B591306076100EEC3A0 /* irrMath.h */, - 95702B5A1306076100EEC3A0 /* irrString.h */, - 95702B5B1306076100EEC3A0 /* irrTypes.h */, - 95702B5C1306076100EEC3A0 /* irrXML.h */, - 95702B5D1306076100EEC3A0 /* ISceneCollisionManager.h */, - 95702B5E1306076100EEC3A0 /* ISceneManager.h */, - 95702B5F1306076100EEC3A0 /* ISceneNode.h */, - 95702B601306076100EEC3A0 /* ISceneNodeAnimator.h */, - 95702B611306076100EEC3A0 /* ISceneNodeAnimatorCameraFPS.h */, - 95702B621306076100EEC3A0 /* ISceneNodeAnimatorCameraMaya.h */, - 95702B631306076100EEC3A0 /* ISceneNodeAnimatorCollisionResponse.h */, - 95702B641306076100EEC3A0 /* ISceneNodeAnimatorFactory.h */, - 95702B651306076100EEC3A0 /* ISceneNodeFactory.h */, - 95702B661306076100EEC3A0 /* ISceneUserDataSerializer.h */, - 95702B671306076100EEC3A0 /* IShaderConstantSetCallBack.h */, - 95702B681306076100EEC3A0 /* IShadowVolumeSceneNode.h */, - 95702B691306076100EEC3A0 /* ISkinnedMesh.h */, - 95702B6A1306076100EEC3A0 /* ITerrainSceneNode.h */, - 95702B6B1306076100EEC3A0 /* ITextSceneNode.h */, - 95702B6C1306076100EEC3A0 /* ITexture.h */, - 95702B6D1306076100EEC3A0 /* ITimer.h */, - 95702B6E1306076100EEC3A0 /* ITriangleSelector.h */, - 95702B6F1306076100EEC3A0 /* IVertexBuffer.h */, - 95702B701306076100EEC3A0 /* IVideoDriver.h */, - 95702B711306076100EEC3A0 /* IVideoModeList.h */, - 95702B721306076100EEC3A0 /* IVolumeLightSceneNode.h */, - 95702B731306076100EEC3A0 /* IWriteFile.h */, - 95702B741306076100EEC3A0 /* IXMLReader.h */, - 95702B751306076100EEC3A0 /* IXMLWriter.h */, - 95702B761306076100EEC3A0 /* Keycodes.h */, - 95702B771306076100EEC3A0 /* line2d.h */, - 95702B781306076100EEC3A0 /* line3d.h */, - 95702B791306076100EEC3A0 /* matrix4.h */, - 95702B7A1306076100EEC3A0 /* path.h */, - 95702B7B1306076100EEC3A0 /* plane3d.h */, - 95702B7C1306076100EEC3A0 /* position2d.h */, - 95702B7D1306076100EEC3A0 /* quaternion.h */, - 95702B7E1306076100EEC3A0 /* rect.h */, - 95702B7F1306076100EEC3A0 /* S3DVertex.h */, - 95702B801306076100EEC3A0 /* SAnimatedMesh.h */, - 95702B811306076100EEC3A0 /* SceneParameters.h */, - 95702B821306076100EEC3A0 /* SColor.h */, - 95702B831306076100EEC3A0 /* SExposedVideoData.h */, - 95702B841306076100EEC3A0 /* SIrrCreationParameters.h */, - 95702B851306076100EEC3A0 /* SKeyMap.h */, - 95702B861306076100EEC3A0 /* SLight.h */, - 95702B871306076100EEC3A0 /* SMaterial.h */, - 95702B881306076100EEC3A0 /* SMaterialLayer.h */, - 95702B891306076100EEC3A0 /* SMesh.h */, - 95702B8A1306076100EEC3A0 /* SMeshBuffer.h */, - 95702B8B1306076100EEC3A0 /* SMeshBufferLightMap.h */, - 95702B8C1306076100EEC3A0 /* SMeshBufferTangents.h */, - 95702B8D1306076100EEC3A0 /* SParticle.h */, - 95702B8E1306076100EEC3A0 /* SSharedMeshBuffer.h */, - 95702B8F1306076100EEC3A0 /* SSkinMeshBuffer.h */, - 95702B901306076100EEC3A0 /* SVertexIndex.h */, - 95702B911306076100EEC3A0 /* SVertexManipulator.h */, - 95702B921306076100EEC3A0 /* SViewFrustum.h */, - 95702B931306076100EEC3A0 /* triangle3d.h */, - 95702B941306076100EEC3A0 /* vector2d.h */, - 95702B951306076100EEC3A0 /* vector3d.h */, - ); - name = Headers; - path = /Library/Frameworks/IrrFramework.framework/Versions/A/Headers; - sourceTree = ""; - }; - 958330B110122B4A00C5137E /* guiengine */ = { - isa = PBXGroup; - children = ( - 950D448A118DEE3C006CFC41 /* CGUISpriteBank.cpp */, - 950D448B118DEE3C006CFC41 /* CGUISpriteBank.h */, - 9583319810123B0200C5137E /* abstract_state_manager.cpp */, - 9583319710123B0200C5137E /* abstract_state_manager.hpp */, - 956039B81218C09E00EB96C4 /* abstract_top_level_container.cpp */, - 956039B91218C09E00EB96C4 /* abstract_top_level_container.hpp */, - 958330B210122B4A00C5137E /* engine.cpp */, - 958330B310122B4A00C5137E /* engine.hpp */, - 958330B410122B4A00C5137E /* event_handler.cpp */, - 958330B510122B4A00C5137E /* event_handler.hpp */, - 9560368A12187EFB00EB96C4 /* layout_manager.cpp */, - 9560368B12187EFB00EB96C4 /* layout_manager.hpp */, - 958330B610122B4A00C5137E /* modaldialog.cpp */, - 958330B710122B4A00C5137E /* modaldialog.hpp */, - 953F8B1F11F7C13C00205E66 /* scalable_font.cpp */, - 953F8B2011F7C13C00205E66 /* scalable_font.hpp */, - 958330BA10122B4A00C5137E /* screen.cpp */, - 958330BB10122B4A00C5137E /* screen.hpp */, - 958330BC10122B4A00C5137E /* screen_loader.cpp */, - 958330BD10122B4A00C5137E /* skin.cpp */, - 958330BE10122B4A00C5137E /* skin.hpp */, - 958330BF10122B4A00C5137E /* widget.cpp */, - 958330C010122B4A00C5137E /* widget.hpp */, - 95ECA13C10124E6900D47C5F /* widgets.hpp */, - 95ECA0EB10124C5000D47C5F /* widgets */, - ); - name = guiengine; - path = ../../guiengine; - sourceTree = SOURCE_ROOT; - }; - 958330C110122B4A00C5137E /* states_screens */ = { - isa = PBXGroup; - children = ( - 95833236101243ED00C5137E /* dialogs */, - 9529980115B2070C0028301A /* cutscene_gui.cpp */, - 9529980215B2070C0028301A /* cutscene_gui.hpp */, - 9551B26D11DC0D4D002DD140 /* addons_screen.cpp */, - 9551B26E11DC0D4D002DD140 /* addons_screen.hpp */, - 956541B910DD5F0A00C83E99 /* arenas_screen.cpp */, - 956541BA10DD5F0A00C83E99 /* arenas_screen.hpp */, - 958330C210122B4A00C5137E /* credits.cpp */, - 958330C310122B4A00C5137E /* credits.hpp */, - 95D2343E1078227A00625256 /* feature_unlocked.cpp */, - 95D2343D1078227A00625256 /* feature_unlocked.hpp */, - 9586318011B1EC9F00B8B4AF /* grand_prix_lose.cpp */, - 9586318111B1EC9F00B8B4AF /* grand_prix_lose.hpp */, - 9586318211B1EC9F00B8B4AF /* grand_prix_win.cpp */, - 9586318311B1EC9F00B8B4AF /* grand_prix_win.hpp */, - 9522F1DE10795E8A0067ECF5 /* help_screen_1.cpp */, - 9522F1DF10795E8A0067ECF5 /* help_screen_1.hpp */, - 9522F1E210795EFF0067ECF5 /* help_screen_2.cpp */, - 9522F1E110795EFF0067ECF5 /* help_screen_2.hpp */, - 9522F1E410795EFF0067ECF5 /* help_screen_3.cpp */, - 9522F1E310795EFF0067ECF5 /* help_screen_3.hpp */, - 95017B3F124698C400C90D56 /* help_screen_4.cpp */, - 95017B40124698C400C90D56 /* help_screen_4.hpp */, - 958330C410122B4A00C5137E /* kart_selection.cpp */, - 958330C510122B4A00C5137E /* kart_selection.hpp */, - 9522F124107948AD0067ECF5 /* main_menu_screen.cpp */, - 9522F123107948AD0067ECF5 /* main_menu_screen.hpp */, - 9592DC6B13021B350039DBC8 /* minimal_race_gui.cpp */, - 9592DC6C13021B350039DBC8 /* minimal_race_gui.hpp */, - 9556A87C119EF976009C558F /* options_screen_audio.cpp */, - 9556A87D119EF976009C558F /* options_screen_audio.hpp */, - 9522F1EB107961560067ECF5 /* options_screen_input.cpp */, - 9522F1EE107961560067ECF5 /* options_screen_input.hpp */, - 950D45CF118E040E006CFC41 /* options_screen_input2.cpp */, - 950D45D0118E040E006CFC41 /* options_screen_input2.hpp */, - 9522F1ED107961560067ECF5 /* options_screen_players.cpp */, - 9522F1EC107961560067ECF5 /* options_screen_players.hpp */, - 9556A87E119EF976009C558F /* options_screen_video.cpp */, - 9556A87F119EF976009C558F /* options_screen_video.hpp */, - 953A959B13367D6E00D86B4D /* options_screen_ui.cpp */, - 953A959C13367D6E00D86B4D /* options_screen_ui.hpp */, - 958330C810122B4A00C5137E /* race_gui.cpp */, - 958330C910122B4A00C5137E /* race_gui.hpp */, - 956F557313527E4B005C291D /* race_gui_base.cpp */, - 956F557413527E4B005C291D /* race_gui_base.hpp */, - 95EF178C14AFBC91005FFEEB /* race_gui_overworld.cpp */, - 95EF178D14AFBC91005FFEEB /* race_gui_overworld.hpp */, - 9554C4A411F1188000906416 /* race_result_gui.cpp */, - 9554C4A511F1188000906416 /* race_result_gui.hpp */, - 9522F15A107949780067ECF5 /* race_setup_screen.cpp */, - 9522F159107949780067ECF5 /* race_setup_screen.hpp */, - 958330CA10122B4A00C5137E /* state_manager.cpp */, - 958330CB10122B4A00C5137E /* state_manager.hpp */, - 95E5C316148C17E500AD3FCC /* story_mode_lobby.cpp */, - 95E5C317148C17E500AD3FCC /* story_mode_lobby.hpp */, - 9522F15C10794A350067ECF5 /* tracks_screen.cpp */, - 9522F15D10794A350067ECF5 /* tracks_screen.hpp */, - ); - name = states_screens; - path = ../../states_screens; - sourceTree = SOURCE_ROOT; - }; - 95833236101243ED00C5137E /* dialogs */ = { - isa = PBXGroup; - children = ( - 956541DF10DD628C00C83E99 /* add_device_dialog.cpp */, - 956541E010DD628C00C83E99 /* add_device_dialog.hpp */, - 95E6A0BE11DCF37800AE088A /* addons_loading.cpp */, - 95E6A0BF11DCF37800AE088A /* addons_loading.hpp */, - 95E246BC111A2959000C965D /* confirm_resolution_dialog.cpp */, - 95E246BD111A2959000C965D /* confirm_resolution_dialog.hpp */, - 957957A014A3CA3900E72497 /* custom_video_settings.cpp */, - 957957A114A3CA3900E72497 /* custom_video_settings.hpp */, - 95833237101243ED00C5137E /* enter_player_name_dialog.cpp */, - 95833238101243ED00C5137E /* enter_player_name_dialog.hpp */, - 95634EF01126272C009C145D /* gp_info_dialog.cpp */, - 95634EF11126272C009C145D /* gp_info_dialog.hpp */, - 95395A75129DFE130079BCE7 /* message_dialog.cpp */, - 95395A76129DFE130079BCE7 /* message_dialog.hpp */, - 95833239101243ED00C5137E /* player_info_dialog.cpp */, - 9583323A101243ED00C5137E /* player_info_dialog.hpp */, - 9583323B101243ED00C5137E /* press_a_key_dialog.cpp */, - 9583323C101243ED00C5137E /* press_a_key_dialog.hpp */, - 958D8C53104F523000A81934 /* race_paused_dialog.cpp */, - 958D8C52104F523000A81934 /* race_paused_dialog.hpp */, - 9543D59D14D38831000B0888 /* select_challenge.cpp */, - 9543D5A514D3885F000B0888 /* select_challenge.hpp */, - 95E5C333148C19F500AD3FCC /* story_mode_new.cpp */, - 95E5C334148C19F500AD3FCC /* story_mode_new.hpp */, - 9583323D101243ED00C5137E /* track_info_dialog.cpp */, - 9583323E101243ED00C5137E /* track_info_dialog.hpp */, - 95D71FB516BF3C7800C0F691 /* tutorial_message_dialog.cpp */, - 95D71FB616BF3C7800C0F691 /* tutorial_message_dialog.hpp */, - ); - name = dialogs; - path = ../../states_screens/dialogs; - sourceTree = SOURCE_ROOT; - }; - 958444891330F89100CEA60A /* tinygettext */ = { - isa = PBXGroup; - children = ( - 95B0E8AE16A4DC390037391C /* tgt_log.cpp */, - 95B0E8AF16A4DC390037391C /* tgt_log.hpp */, - 9584448A1330F89100CEA60A /* dictionary.cpp */, - 9584448B1330F89100CEA60A /* dictionary.hpp */, - 9584448C1330F89100CEA60A /* dictionary_manager.cpp */, - 9584448D1330F89100CEA60A /* dictionary_manager.hpp */, - 9584448E1330F89100CEA60A /* file_system.hpp */, - 9584448F1330F89100CEA60A /* iconv.cpp */, - 958444901330F89100CEA60A /* iconv.hpp */, - 958444911330F89100CEA60A /* language.cpp */, - 958444921330F89100CEA60A /* language.hpp */, - 958444951330F89100CEA60A /* log_stream.hpp */, - 958444961330F89100CEA60A /* plural_forms.cpp */, - 958444971330F89100CEA60A /* plural_forms.hpp */, - 958444981330F89100CEA60A /* po_parser.cpp */, - 958444991330F89100CEA60A /* po_parser.hpp */, - 9584449A1330F89100CEA60A /* stk_file_system.cpp */, - 9584449B1330F89100CEA60A /* stk_file_system.hpp */, - 9584449C1330F89100CEA60A /* tinygettext.cpp */, - 9584449D1330F89100CEA60A /* tinygettext.hpp */, - ); - name = tinygettext; - path = ../../tinygettext; - sourceTree = SOURCE_ROOT; - }; - 9593A10C1609384700AB5EEB /* Lib */ = { - isa = PBXGroup; - children = ( - 95D71F6316BF380900C0F691 /* wiiuse */, - 9593A2CE1609388C00AB5EEB /* enet */, - 9593A10D1609386100AB5EEB /* bullet */, - ); - name = Lib; - sourceTree = ""; - }; - 9593A10D1609386100AB5EEB /* bullet */ = { - isa = PBXGroup; - children = ( - 9593A1161609386100AB5EEB /* src */, - ); - name = bullet; - path = ../../../lib/bullet; - sourceTree = SOURCE_ROOT; - }; - 9593A1161609386100AB5EEB /* src */ = { - isa = PBXGroup; - children = ( - 9593A1171609386100AB5EEB /* btBulletCollisionCommon.h */, - 9593A1181609386100AB5EEB /* btBulletDynamicsCommon.h */, - 9593A1191609386100AB5EEB /* Bullet-C-Api.h */, - 9593A11A1609386100AB5EEB /* BulletCollision */, - 9593A1F51609386100AB5EEB /* BulletDynamics */, - 9593A22A1609386100AB5EEB /* LinearMath */, - 9593A24A1609386100AB5EEB /* Makefile.am */, - ); - name = src; - path = games/supertuxkart/lib/bullet/src; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A11A1609386100AB5EEB /* BulletCollision */ = { - isa = PBXGroup; - children = ( - 9593A11B1609386100AB5EEB /* BroadphaseCollision */, - 9593A1321609386100AB5EEB /* CollisionDispatch */, - 9593A1631609386100AB5EEB /* CollisionShapes */, - 9593A1B01609386100AB5EEB /* Gimpact */, - 9593A1D71609386100AB5EEB /* NarrowPhaseCollision */, - ); - name = BulletCollision; - path = games/supertuxkart/lib/bullet/src/BulletCollision; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A11B1609386100AB5EEB /* BroadphaseCollision */ = { - isa = PBXGroup; - children = ( - 9593A11C1609386100AB5EEB /* btAxisSweep3.cpp */, - 9593A11D1609386100AB5EEB /* btAxisSweep3.h */, - 9593A11E1609386100AB5EEB /* btBroadphaseInterface.h */, - 9593A11F1609386100AB5EEB /* btBroadphaseProxy.cpp */, - 9593A1201609386100AB5EEB /* btBroadphaseProxy.h */, - 9593A1211609386100AB5EEB /* btCollisionAlgorithm.cpp */, - 9593A1221609386100AB5EEB /* btCollisionAlgorithm.h */, - 9593A1231609386100AB5EEB /* btDbvt.cpp */, - 9593A1241609386100AB5EEB /* btDbvt.h */, - 9593A1251609386100AB5EEB /* btDbvtBroadphase.cpp */, - 9593A1261609386100AB5EEB /* btDbvtBroadphase.h */, - 9593A1271609386100AB5EEB /* btDispatcher.cpp */, - 9593A1281609386100AB5EEB /* btDispatcher.h */, - 9593A1291609386100AB5EEB /* btMultiSapBroadphase.cpp */, - 9593A12A1609386100AB5EEB /* btMultiSapBroadphase.h */, - 9593A12B1609386100AB5EEB /* btOverlappingPairCache.cpp */, - 9593A12C1609386100AB5EEB /* btOverlappingPairCache.h */, - 9593A12D1609386100AB5EEB /* btOverlappingPairCallback.h */, - 9593A12E1609386100AB5EEB /* btQuantizedBvh.cpp */, - 9593A12F1609386100AB5EEB /* btQuantizedBvh.h */, - 9593A1301609386100AB5EEB /* btSimpleBroadphase.cpp */, - 9593A1311609386100AB5EEB /* btSimpleBroadphase.h */, - ); - name = BroadphaseCollision; - path = games/supertuxkart/lib/bullet/src/BulletCollision/BroadphaseCollision; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A1321609386100AB5EEB /* CollisionDispatch */ = { - isa = PBXGroup; - children = ( - 9593A1331609386100AB5EEB /* btActivatingCollisionAlgorithm.cpp */, - 9593A1341609386100AB5EEB /* btActivatingCollisionAlgorithm.h */, - 9593A1351609386100AB5EEB /* btBox2dBox2dCollisionAlgorithm.cpp */, - 9593A1361609386100AB5EEB /* btBox2dBox2dCollisionAlgorithm.h */, - 9593A1371609386100AB5EEB /* btBoxBoxCollisionAlgorithm.cpp */, - 9593A1381609386100AB5EEB /* btBoxBoxCollisionAlgorithm.h */, - 9593A1391609386100AB5EEB /* btBoxBoxDetector.cpp */, - 9593A13A1609386100AB5EEB /* btBoxBoxDetector.h */, - 9593A13B1609386100AB5EEB /* btCollisionConfiguration.h */, - 9593A13C1609386100AB5EEB /* btCollisionCreateFunc.h */, - 9593A13D1609386100AB5EEB /* btCollisionDispatcher.cpp */, - 9593A13E1609386100AB5EEB /* btCollisionDispatcher.h */, - 9593A13F1609386100AB5EEB /* btCollisionObject.cpp */, - 9593A1401609386100AB5EEB /* btCollisionObject.h */, - 9593A1411609386100AB5EEB /* btCollisionWorld.cpp */, - 9593A1421609386100AB5EEB /* btCollisionWorld.h */, - 9593A1431609386100AB5EEB /* btCompoundCollisionAlgorithm.cpp */, - 9593A1441609386100AB5EEB /* btCompoundCollisionAlgorithm.h */, - 9593A1451609386100AB5EEB /* btConvex2dConvex2dAlgorithm.cpp */, - 9593A1461609386100AB5EEB /* btConvex2dConvex2dAlgorithm.h */, - 9593A1471609386100AB5EEB /* btConvexConcaveCollisionAlgorithm.cpp */, - 9593A1481609386100AB5EEB /* btConvexConcaveCollisionAlgorithm.h */, - 9593A1491609386100AB5EEB /* btConvexConvexAlgorithm.cpp */, - 9593A14A1609386100AB5EEB /* btConvexConvexAlgorithm.h */, - 9593A14B1609386100AB5EEB /* btConvexPlaneCollisionAlgorithm.cpp */, - 9593A14C1609386100AB5EEB /* btConvexPlaneCollisionAlgorithm.h */, - 9593A14D1609386100AB5EEB /* btDefaultCollisionConfiguration.cpp */, - 9593A14E1609386100AB5EEB /* btDefaultCollisionConfiguration.h */, - 9593A14F1609386100AB5EEB /* btEmptyCollisionAlgorithm.cpp */, - 9593A1501609386100AB5EEB /* btEmptyCollisionAlgorithm.h */, - 9593A1511609386100AB5EEB /* btGhostObject.cpp */, - 9593A1521609386100AB5EEB /* btGhostObject.h */, - 9593A1531609386100AB5EEB /* btInternalEdgeUtility.cpp */, - 9593A1541609386100AB5EEB /* btInternalEdgeUtility.h */, - 9593A1551609386100AB5EEB /* btManifoldResult.cpp */, - 9593A1561609386100AB5EEB /* btManifoldResult.h */, - 9593A1571609386100AB5EEB /* btSimulationIslandManager.cpp */, - 9593A1581609386100AB5EEB /* btSimulationIslandManager.h */, - 9593A1591609386100AB5EEB /* btSphereBoxCollisionAlgorithm.cpp */, - 9593A15A1609386100AB5EEB /* btSphereBoxCollisionAlgorithm.h */, - 9593A15B1609386100AB5EEB /* btSphereSphereCollisionAlgorithm.cpp */, - 9593A15C1609386100AB5EEB /* btSphereSphereCollisionAlgorithm.h */, - 9593A15D1609386100AB5EEB /* btSphereTriangleCollisionAlgorithm.cpp */, - 9593A15E1609386100AB5EEB /* btSphereTriangleCollisionAlgorithm.h */, - 9593A15F1609386100AB5EEB /* btUnionFind.cpp */, - 9593A1601609386100AB5EEB /* btUnionFind.h */, - 9593A1611609386100AB5EEB /* SphereTriangleDetector.cpp */, - 9593A1621609386100AB5EEB /* SphereTriangleDetector.h */, - ); - name = CollisionDispatch; - path = games/supertuxkart/lib/bullet/src/BulletCollision/CollisionDispatch; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A1631609386100AB5EEB /* CollisionShapes */ = { - isa = PBXGroup; - children = ( - 9593A1641609386100AB5EEB /* btBox2dShape.cpp */, - 9593A1651609386100AB5EEB /* btBox2dShape.h */, - 9593A1661609386100AB5EEB /* btBoxShape.cpp */, - 9593A1671609386100AB5EEB /* btBoxShape.h */, - 9593A1681609386100AB5EEB /* btBvhTriangleMeshShape.cpp */, - 9593A1691609386100AB5EEB /* btBvhTriangleMeshShape.h */, - 9593A16A1609386100AB5EEB /* btCapsuleShape.cpp */, - 9593A16B1609386100AB5EEB /* btCapsuleShape.h */, - 9593A16C1609386100AB5EEB /* btCollisionMargin.h */, - 9593A16D1609386100AB5EEB /* btCollisionShape.cpp */, - 9593A16E1609386100AB5EEB /* btCollisionShape.h */, - 9593A16F1609386100AB5EEB /* btCompoundShape.cpp */, - 9593A1701609386100AB5EEB /* btCompoundShape.h */, - 9593A1711609386100AB5EEB /* btConcaveShape.cpp */, - 9593A1721609386100AB5EEB /* btConcaveShape.h */, - 9593A1731609386100AB5EEB /* btConeShape.cpp */, - 9593A1741609386100AB5EEB /* btConeShape.h */, - 9593A1751609386100AB5EEB /* btConvex2dShape.cpp */, - 9593A1761609386100AB5EEB /* btConvex2dShape.h */, - 9593A1771609386100AB5EEB /* btConvexHullShape.cpp */, - 9593A1781609386100AB5EEB /* btConvexHullShape.h */, - 9593A1791609386100AB5EEB /* btConvexInternalShape.cpp */, - 9593A17A1609386100AB5EEB /* btConvexInternalShape.h */, - 9593A17B1609386100AB5EEB /* btConvexPointCloudShape.cpp */, - 9593A17C1609386100AB5EEB /* btConvexPointCloudShape.h */, - 9593A17D1609386100AB5EEB /* btConvexPolyhedron.cpp */, - 9593A17E1609386100AB5EEB /* btConvexPolyhedron.h */, - 9593A17F1609386100AB5EEB /* btConvexShape.cpp */, - 9593A1801609386100AB5EEB /* btConvexShape.h */, - 9593A1811609386100AB5EEB /* btConvexTriangleMeshShape.cpp */, - 9593A1821609386100AB5EEB /* btConvexTriangleMeshShape.h */, - 9593A1831609386100AB5EEB /* btCylinderShape.cpp */, - 9593A1841609386100AB5EEB /* btCylinderShape.h */, - 9593A1851609386100AB5EEB /* btEmptyShape.cpp */, - 9593A1861609386100AB5EEB /* btEmptyShape.h */, - 9593A1871609386100AB5EEB /* btHeightfieldTerrainShape.cpp */, - 9593A1881609386100AB5EEB /* btHeightfieldTerrainShape.h */, - 9593A1891609386100AB5EEB /* btMaterial.h */, - 9593A18A1609386100AB5EEB /* btMinkowskiSumShape.cpp */, - 9593A18B1609386100AB5EEB /* btMinkowskiSumShape.h */, - 9593A18C1609386100AB5EEB /* btMultimaterialTriangleMeshShape.cpp */, - 9593A18D1609386100AB5EEB /* btMultimaterialTriangleMeshShape.h */, - 9593A18E1609386100AB5EEB /* btMultiSphereShape.cpp */, - 9593A18F1609386100AB5EEB /* btMultiSphereShape.h */, - 9593A1901609386100AB5EEB /* btOptimizedBvh.cpp */, - 9593A1911609386100AB5EEB /* btOptimizedBvh.h */, - 9593A1921609386100AB5EEB /* btPolyhedralConvexShape.cpp */, - 9593A1931609386100AB5EEB /* btPolyhedralConvexShape.h */, - 9593A1941609386100AB5EEB /* btScaledBvhTriangleMeshShape.cpp */, - 9593A1951609386100AB5EEB /* btScaledBvhTriangleMeshShape.h */, - 9593A1961609386100AB5EEB /* btShapeHull.cpp */, - 9593A1971609386100AB5EEB /* btShapeHull.h */, - 9593A1981609386100AB5EEB /* btSphereShape.cpp */, - 9593A1991609386100AB5EEB /* btSphereShape.h */, - 9593A19A1609386100AB5EEB /* btStaticPlaneShape.cpp */, - 9593A19B1609386100AB5EEB /* btStaticPlaneShape.h */, - 9593A19C1609386100AB5EEB /* btStridingMeshInterface.cpp */, - 9593A19D1609386100AB5EEB /* btStridingMeshInterface.h */, - 9593A19E1609386100AB5EEB /* btTetrahedronShape.cpp */, - 9593A19F1609386100AB5EEB /* btTetrahedronShape.h */, - 9593A1A01609386100AB5EEB /* btTriangleBuffer.cpp */, - 9593A1A11609386100AB5EEB /* btTriangleBuffer.h */, - 9593A1A21609386100AB5EEB /* btTriangleCallback.cpp */, - 9593A1A31609386100AB5EEB /* btTriangleCallback.h */, - 9593A1A41609386100AB5EEB /* btTriangleIndexVertexArray.cpp */, - 9593A1A51609386100AB5EEB /* btTriangleIndexVertexArray.h */, - 9593A1A61609386100AB5EEB /* btTriangleIndexVertexMaterialArray.cpp */, - 9593A1A71609386100AB5EEB /* btTriangleIndexVertexMaterialArray.h */, - 9593A1A81609386100AB5EEB /* btTriangleInfoMap.h */, - 9593A1A91609386100AB5EEB /* btTriangleMesh.cpp */, - 9593A1AA1609386100AB5EEB /* btTriangleMesh.h */, - 9593A1AB1609386100AB5EEB /* btTriangleMeshShape.cpp */, - 9593A1AC1609386100AB5EEB /* btTriangleMeshShape.h */, - 9593A1AD1609386100AB5EEB /* btTriangleShape.h */, - 9593A1AE1609386100AB5EEB /* btUniformScalingShape.cpp */, - 9593A1AF1609386100AB5EEB /* btUniformScalingShape.h */, - ); - name = CollisionShapes; - path = games/supertuxkart/lib/bullet/src/BulletCollision/CollisionShapes; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A1B01609386100AB5EEB /* Gimpact */ = { - isa = PBXGroup; - children = ( - 9593A1B11609386100AB5EEB /* btBoxCollision.h */, - 9593A1B21609386100AB5EEB /* btClipPolygon.h */, - 9593A1B31609386100AB5EEB /* btContactProcessing.cpp */, - 9593A1B41609386100AB5EEB /* btContactProcessing.h */, - 9593A1B51609386100AB5EEB /* btGenericPoolAllocator.cpp */, - 9593A1B61609386100AB5EEB /* btGenericPoolAllocator.h */, - 9593A1B71609386100AB5EEB /* btGeometryOperations.h */, - 9593A1B81609386100AB5EEB /* btGImpactBvh.cpp */, - 9593A1B91609386100AB5EEB /* btGImpactBvh.h */, - 9593A1BA1609386100AB5EEB /* btGImpactCollisionAlgorithm.cpp */, - 9593A1BB1609386100AB5EEB /* btGImpactCollisionAlgorithm.h */, - 9593A1BC1609386100AB5EEB /* btGImpactMassUtil.h */, - 9593A1BD1609386100AB5EEB /* btGImpactQuantizedBvh.cpp */, - 9593A1BE1609386100AB5EEB /* btGImpactQuantizedBvh.h */, - 9593A1BF1609386100AB5EEB /* btGImpactShape.cpp */, - 9593A1C01609386100AB5EEB /* btGImpactShape.h */, - 9593A1C11609386100AB5EEB /* btQuantization.h */, - 9593A1C21609386100AB5EEB /* btTriangleShapeEx.cpp */, - 9593A1C31609386100AB5EEB /* btTriangleShapeEx.h */, - 9593A1C41609386100AB5EEB /* gim_array.h */, - 9593A1C51609386100AB5EEB /* gim_basic_geometry_operations.h */, - 9593A1C61609386100AB5EEB /* gim_bitset.h */, - 9593A1C71609386100AB5EEB /* gim_box_collision.h */, - 9593A1C81609386100AB5EEB /* gim_box_set.cpp */, - 9593A1C91609386100AB5EEB /* gim_box_set.h */, - 9593A1CA1609386100AB5EEB /* gim_clip_polygon.h */, - 9593A1CB1609386100AB5EEB /* gim_contact.cpp */, - 9593A1CC1609386100AB5EEB /* gim_contact.h */, - 9593A1CD1609386100AB5EEB /* gim_geom_types.h */, - 9593A1CE1609386100AB5EEB /* gim_geometry.h */, - 9593A1CF1609386100AB5EEB /* gim_hash_table.h */, - 9593A1D01609386100AB5EEB /* gim_linear_math.h */, - 9593A1D11609386100AB5EEB /* gim_math.h */, - 9593A1D21609386100AB5EEB /* gim_memory.cpp */, - 9593A1D31609386100AB5EEB /* gim_memory.h */, - 9593A1D41609386100AB5EEB /* gim_radixsort.h */, - 9593A1D51609386100AB5EEB /* gim_tri_collision.cpp */, - 9593A1D61609386100AB5EEB /* gim_tri_collision.h */, - ); - name = Gimpact; - path = games/supertuxkart/lib/bullet/src/BulletCollision/Gimpact; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A1D71609386100AB5EEB /* NarrowPhaseCollision */ = { - isa = PBXGroup; - children = ( - 9593A1D81609386100AB5EEB /* btContinuousConvexCollision.cpp */, - 9593A1D91609386100AB5EEB /* btContinuousConvexCollision.h */, - 9593A1DA1609386100AB5EEB /* btConvexCast.cpp */, - 9593A1DB1609386100AB5EEB /* btConvexCast.h */, - 9593A1DC1609386100AB5EEB /* btConvexPenetrationDepthSolver.h */, - 9593A1DD1609386100AB5EEB /* btDiscreteCollisionDetectorInterface.h */, - 9593A1DE1609386100AB5EEB /* btGjkConvexCast.cpp */, - 9593A1DF1609386100AB5EEB /* btGjkConvexCast.h */, - 9593A1E01609386100AB5EEB /* btGjkEpa2.cpp */, - 9593A1E11609386100AB5EEB /* btGjkEpa2.h */, - 9593A1E21609386100AB5EEB /* btGjkEpaPenetrationDepthSolver.cpp */, - 9593A1E31609386100AB5EEB /* btGjkEpaPenetrationDepthSolver.h */, - 9593A1E41609386100AB5EEB /* btGjkPairDetector.cpp */, - 9593A1E51609386100AB5EEB /* btGjkPairDetector.h */, - 9593A1E61609386100AB5EEB /* btManifoldPoint.h */, - 9593A1E71609386100AB5EEB /* btMinkowskiPenetrationDepthSolver.cpp */, - 9593A1E81609386100AB5EEB /* btMinkowskiPenetrationDepthSolver.h */, - 9593A1E91609386100AB5EEB /* btPersistentManifold.cpp */, - 9593A1EA1609386100AB5EEB /* btPersistentManifold.h */, - 9593A1EB1609386100AB5EEB /* btPointCollector.h */, - 9593A1EC1609386100AB5EEB /* btPolyhedralContactClipping.cpp */, - 9593A1ED1609386100AB5EEB /* btPolyhedralContactClipping.h */, - 9593A1EE1609386100AB5EEB /* btRaycastCallback.cpp */, - 9593A1EF1609386100AB5EEB /* btRaycastCallback.h */, - 9593A1F01609386100AB5EEB /* btSimplexSolverInterface.h */, - 9593A1F11609386100AB5EEB /* btSubSimplexConvexCast.cpp */, - 9593A1F21609386100AB5EEB /* btSubSimplexConvexCast.h */, - 9593A1F31609386100AB5EEB /* btVoronoiSimplexSolver.cpp */, - 9593A1F41609386100AB5EEB /* btVoronoiSimplexSolver.h */, - ); - name = NarrowPhaseCollision; - path = games/supertuxkart/lib/bullet/src/BulletCollision/NarrowPhaseCollision; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A1F51609386100AB5EEB /* BulletDynamics */ = { - isa = PBXGroup; - children = ( - 9593A1F61609386100AB5EEB /* Character */, - 9593A1FA1609386100AB5EEB /* ConstraintSolver */, - 9593A2181609386100AB5EEB /* Dynamics */, - 9593A2241609386100AB5EEB /* Vehicle */, - ); - name = BulletDynamics; - path = games/supertuxkart/lib/bullet/src/BulletDynamics; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A1F61609386100AB5EEB /* Character */ = { - isa = PBXGroup; - children = ( - 9593A1F71609386100AB5EEB /* btCharacterControllerInterface.h */, - 9593A1F81609386100AB5EEB /* btKinematicCharacterController.cpp */, - 9593A1F91609386100AB5EEB /* btKinematicCharacterController.h */, - ); - name = Character; - path = games/supertuxkart/lib/bullet/src/BulletDynamics/Character; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A1FA1609386100AB5EEB /* ConstraintSolver */ = { - isa = PBXGroup; - children = ( - 9593A1FB1609386100AB5EEB /* btConeTwistConstraint.cpp */, - 9593A1FC1609386100AB5EEB /* btConeTwistConstraint.h */, - 9593A1FD1609386100AB5EEB /* btConstraintSolver.h */, - 9593A1FE1609386100AB5EEB /* btContactConstraint.cpp */, - 9593A1FF1609386100AB5EEB /* btContactConstraint.h */, - 9593A2001609386100AB5EEB /* btContactSolverInfo.h */, - 9593A2011609386100AB5EEB /* btGeneric6DofConstraint.cpp */, - 9593A2021609386100AB5EEB /* btGeneric6DofConstraint.h */, - 9593A2031609386100AB5EEB /* btGeneric6DofSpringConstraint.cpp */, - 9593A2041609386100AB5EEB /* btGeneric6DofSpringConstraint.h */, - 9593A2051609386100AB5EEB /* btHinge2Constraint.cpp */, - 9593A2061609386100AB5EEB /* btHinge2Constraint.h */, - 9593A2071609386100AB5EEB /* btHingeConstraint.cpp */, - 9593A2081609386100AB5EEB /* btHingeConstraint.h */, - 9593A2091609386100AB5EEB /* btJacobianEntry.h */, - 9593A20A1609386100AB5EEB /* btPoint2PointConstraint.cpp */, - 9593A20B1609386100AB5EEB /* btPoint2PointConstraint.h */, - 9593A20C1609386100AB5EEB /* btSequentialImpulseConstraintSolver.cpp */, - 9593A20D1609386100AB5EEB /* btSequentialImpulseConstraintSolver.h */, - 9593A20E1609386100AB5EEB /* btSliderConstraint.cpp */, - 9593A20F1609386100AB5EEB /* btSliderConstraint.h */, - 9593A2101609386100AB5EEB /* btSolve2LinearConstraint.cpp */, - 9593A2111609386100AB5EEB /* btSolve2LinearConstraint.h */, - 9593A2121609386100AB5EEB /* btSolverBody.h */, - 9593A2131609386100AB5EEB /* btSolverConstraint.h */, - 9593A2141609386100AB5EEB /* btTypedConstraint.cpp */, - 9593A2151609386100AB5EEB /* btTypedConstraint.h */, - 9593A2161609386100AB5EEB /* btUniversalConstraint.cpp */, - 9593A2171609386100AB5EEB /* btUniversalConstraint.h */, - ); - name = ConstraintSolver; - path = games/supertuxkart/lib/bullet/src/BulletDynamics/ConstraintSolver; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A2181609386100AB5EEB /* Dynamics */ = { - isa = PBXGroup; - children = ( - 9593A2191609386100AB5EEB /* btActionInterface.h */, - 9593A21A1609386100AB5EEB /* btContinuousDynamicsWorld.cpp */, - 9593A21B1609386100AB5EEB /* btContinuousDynamicsWorld.h */, - 9593A21C1609386100AB5EEB /* btDiscreteDynamicsWorld.cpp */, - 9593A21D1609386100AB5EEB /* btDiscreteDynamicsWorld.h */, - 9593A21E1609386100AB5EEB /* btDynamicsWorld.h */, - 9593A21F1609386100AB5EEB /* btRigidBody.cpp */, - 9593A2201609386100AB5EEB /* btRigidBody.h */, - 9593A2211609386100AB5EEB /* btSimpleDynamicsWorld.cpp */, - 9593A2221609386100AB5EEB /* btSimpleDynamicsWorld.h */, - 9593A2231609386100AB5EEB /* Bullet-C-API.cpp */, - ); - name = Dynamics; - path = games/supertuxkart/lib/bullet/src/BulletDynamics/Dynamics; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A2241609386100AB5EEB /* Vehicle */ = { - isa = PBXGroup; - children = ( - 9593A2251609386100AB5EEB /* btRaycastVehicle.cpp */, - 9593A2261609386100AB5EEB /* btRaycastVehicle.h */, - 9593A2271609386100AB5EEB /* btVehicleRaycaster.h */, - 9593A2281609386100AB5EEB /* btWheelInfo.cpp */, - 9593A2291609386100AB5EEB /* btWheelInfo.h */, - ); - name = Vehicle; - path = games/supertuxkart/lib/bullet/src/BulletDynamics/Vehicle; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A22A1609386100AB5EEB /* LinearMath */ = { - isa = PBXGroup; - children = ( - 9593A22B1609386100AB5EEB /* btAabbUtil2.h */, - 9593A22C1609386100AB5EEB /* btAlignedAllocator.cpp */, - 9593A22D1609386100AB5EEB /* btAlignedAllocator.h */, - 9593A22E1609386100AB5EEB /* btAlignedObjectArray.h */, - 9593A22F1609386100AB5EEB /* btConvexHull.cpp */, - 9593A2301609386100AB5EEB /* btConvexHull.h */, - 9593A2311609386100AB5EEB /* btConvexHullComputer.cpp */, - 9593A2321609386100AB5EEB /* btConvexHullComputer.h */, - 9593A2331609386100AB5EEB /* btDefaultMotionState.h */, - 9593A2341609386100AB5EEB /* btGeometryUtil.cpp */, - 9593A2351609386100AB5EEB /* btGeometryUtil.h */, - 9593A2361609386100AB5EEB /* btGrahamScan2dConvexHull.h */, - 9593A2371609386100AB5EEB /* btHashMap.h */, - 9593A2381609386100AB5EEB /* btIDebugDraw.h */, - 9593A2391609386100AB5EEB /* btList.h */, - 9593A23A1609386100AB5EEB /* btMatrix3x3.h */, - 9593A23B1609386100AB5EEB /* btMinMax.h */, - 9593A23C1609386100AB5EEB /* btMotionState.h */, - 9593A23D1609386100AB5EEB /* btPoolAllocator.h */, - 9593A23E1609386100AB5EEB /* btQuadWord.h */, - 9593A23F1609386100AB5EEB /* btQuaternion.h */, - 9593A2401609386100AB5EEB /* btQuickprof.cpp */, - 9593A2411609386100AB5EEB /* btQuickprof.h */, - 9593A2421609386100AB5EEB /* btRandom.h */, - 9593A2431609386100AB5EEB /* btScalar.h */, - 9593A2441609386100AB5EEB /* btSerializer.cpp */, - 9593A2451609386100AB5EEB /* btSerializer.h */, - 9593A2461609386100AB5EEB /* btStackAlloc.h */, - 9593A2471609386100AB5EEB /* btTransform.h */, - 9593A2481609386100AB5EEB /* btTransformUtil.h */, - 9593A2491609386100AB5EEB /* btVector3.h */, - ); - name = LinearMath; - path = games/supertuxkart/lib/bullet/src/LinearMath; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A2CE1609388C00AB5EEB /* enet */ = { - isa = PBXGroup; - children = ( - 9593A2CF1609388C00AB5EEB /* aclocal.m4 */, - 9593A2D01609388C00AB5EEB /* callbacks.c */, - 9593A2D11609388C00AB5EEB /* ChangeLog */, - 9593A2D21609388C00AB5EEB /* CMakeLists.txt */, - 9593A2D31609388C00AB5EEB /* compress.c */, - 9593A2D41609388C00AB5EEB /* configure */, - 9593A2D51609388C00AB5EEB /* configure.in */, - 9593A2D61609388C00AB5EEB /* depcomp */, - 9593A2D71609388C00AB5EEB /* design.txt */, - 9593A2DF1609388C00AB5EEB /* Doxyfile */, - 9593A2E01609388C00AB5EEB /* enet.dsp */, - 9593A2E11609388C00AB5EEB /* host.c */, - 9593A2E21609388C00AB5EEB /* include */, - 9593A2ED1609388C00AB5EEB /* install-sh */, - 9593A2EE1609388C00AB5EEB /* LICENSE */, - 9593A2EF1609388C00AB5EEB /* list.c */, - 9593A2F01609388C00AB5EEB /* Makefile.am */, - 9593A2F11609388C00AB5EEB /* missing */, - 9593A2F21609388C00AB5EEB /* packet.c */, - 9593A2F31609388C00AB5EEB /* peer.c */, - 9593A2F41609388C00AB5EEB /* protocol.c */, - 9593A2F51609388C00AB5EEB /* README */, - 9593A2F61609388C00AB5EEB /* tutorial.txt */, - 9593A2F71609388C00AB5EEB /* unix.c */, - 9593A2F81609388C00AB5EEB /* win32.c */, - ); - name = enet; - path = ../../../lib/enet; - sourceTree = SOURCE_ROOT; - }; - 9593A2E21609388C00AB5EEB /* include */ = { - isa = PBXGroup; - children = ( - 9593A2E31609388C00AB5EEB /* enet */, - ); - name = include; - path = games/supertuxkart/lib/enet/include; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 9593A2E31609388C00AB5EEB /* enet */ = { - isa = PBXGroup; - children = ( - 9593A2E41609388C00AB5EEB /* callbacks.h */, - 9593A2E51609388C00AB5EEB /* enet.h */, - 9593A2E61609388C00AB5EEB /* list.h */, - 9593A2E71609388C00AB5EEB /* protocol.h */, - 9593A2E81609388C00AB5EEB /* time.h */, - 9593A2E91609388C00AB5EEB /* types.h */, - 9593A2EA1609388C00AB5EEB /* unix.h */, - 9593A2EB1609388C00AB5EEB /* utility.h */, - 9593A2EC1609388C00AB5EEB /* win32.h */, - ); - name = enet; - path = games/supertuxkart/lib/enet/include/enet; - sourceTree = SYSTEM_DEVELOPER_DIR; - }; - 95A118280F77EA3100B18B3D /* input */ = { - isa = PBXGroup; - children = ( - 95D71FB016BF3A9E00C0F691 /* wiimote_manager.cpp */, - 95D71FB116BF3A9E00C0F691 /* wiimote_manager.hpp */, - 9528CC221291E7A10078A5EF /* binding.cpp */, - 9528CC231291E7A10078A5EF /* binding.hpp */, - 95A1187A0F78024E00B18B3D /* device_manager.cpp */, - 95A1187C0F78026D00B18B3D /* device_manager.hpp */, - 95A118290F77EA3100B18B3D /* input.hpp */, - 95A1184A0F77FC3900B18B3D /* input_device.cpp */, - 95A1184C0F77FC8800B18B3D /* input_device.hpp */, - 95A1182A0F77EA3100B18B3D /* input_manager.cpp */, - 95A1182B0F77EA3100B18B3D /* input_manager.hpp */, - ); - name = input; - path = ../../input; - sourceTree = SOURCE_ROOT; - }; - 95A197871502E5020074B892 /* replay */ = { - isa = PBXGroup; - children = ( - 95A197881502E5020074B892 /* replay_base.cpp */, - 95A197891502E5020074B892 /* replay_base.hpp */, - 95A1978A1502E5020074B892 /* replay_play.cpp */, - 95A1978B1502E5020074B892 /* replay_play.hpp */, - 95A1978C1502E5020074B892 /* replay_recorder.cpp */, - 95A1978D1502E5020074B892 /* replay_recorder.hpp */, - ); - name = replay; - path = ../../replay; - sourceTree = SOURCE_ROOT; - }; - 95C2ABA60F29653F000D3E5D /* src */ = { - isa = PBXGroup; - children = ( - 9551B27311DC0D6A002DD140 /* addons */, - 954E4C240FF98B6E0047FE3E /* animations */, - 95C2AC260F296540000D3E5D /* audio */, - 95C2AE250F296541000D3E5D /* challenges */, - 95D950CC0FE473CA002E10AD /* config */, - 952A152C103F66D600B1895D /* graphics */, - 958330B110122B4A00C5137E /* guiengine */, - 95C65D750F532F7D00BE7BA7 /* io */, - 95C2B0F50F296545000D3E5D /* items */, - 95A118280F77EA3100B18B3D /* input */, - 95C2B1160F296545000D3E5D /* karts */, - 95C2B1470F296545000D3E5D /* modes */, - 95C2B1590F296545000D3E5D /* network */, - 95C2B17F0F296545000D3E5D /* physics */, - 95263DDF0FD7471900CF5F92 /* race */, - 95A197871502E5020074B892 /* replay */, - 958330C110122B4A00C5137E /* states_screens */, - 958444891330F89100CEA60A /* tinygettext */, - 95C2B1CE0F296545000D3E5D /* tracks */, - 95C2B1DF0F296546000D3E5D /* utils */, - 95C2B1360F296545000D3E5D /* main.cpp */, - 95C2B1380F296545000D3E5D /* main_loop.cpp */, - 95C2B1390F296545000D3E5D /* main_loop.hpp */, - ); - name = src; - path = ../..; - sourceTree = SOURCE_ROOT; - }; - 95C2AC260F296540000D3E5D /* audio */ = { - isa = PBXGroup; - children = ( - 95A540581481C4760086FE38 /* dummy_sfx.hpp */, - 95C2AC270F296540000D3E5D /* music.hpp */, - 95A5405B1481C4DB0086FE38 /* music_dummy.hpp */, - 95C2AC280F296540000D3E5D /* music_information.cpp */, - 95C2AC290F296540000D3E5D /* music_information.hpp */, - 958BD76E117F6AE90095B483 /* music_manager.cpp */, - 958BD76F117F6AE90095B483 /* music_manager.hpp */, - 95C2AC2A0F296540000D3E5D /* music_ogg.cpp */, - 95C2AC2B0F296540000D3E5D /* music_ogg.hpp */, - 95C2AC2C0F296540000D3E5D /* sfx_base.hpp */, - 9516B07C12629C4E005F9493 /* sfx_buffer.cpp */, - 9516B07D12629C4E005F9493 /* sfx_buffer.hpp */, - 95C2AC2D0F296540000D3E5D /* sfx_manager.cpp */, - 95C2AC2E0F296540000D3E5D /* sfx_manager.hpp */, - 95C2AC2F0F296540000D3E5D /* sfx_openal.cpp */, - 95C2AC300F296540000D3E5D /* sfx_openal.hpp */, - ); - name = audio; - path = ../../audio; - sourceTree = SOURCE_ROOT; - }; - 95C2AE250F296541000D3E5D /* challenges */ = { - isa = PBXGroup; - children = ( - 95C2AE260F296541000D3E5D /* challenge.cpp */, - 95C2AE270F296541000D3E5D /* challenge.hpp */, - 95C2AE280F296541000D3E5D /* challenge_data.cpp */, - 95C2AE290F296541000D3E5D /* challenge_data.hpp */, - 95E5C3C0148C418100AD3FCC /* game_slot.cpp */, - 95E5C3C1148C418100AD3FCC /* game_slot.hpp */, - 95C2AE2A0F296541000D3E5D /* unlock_manager.cpp */, - 95C2AE2B0F296541000D3E5D /* unlock_manager.hpp */, - ); - name = challenges; - path = ../../challenges; - sourceTree = SOURCE_ROOT; - }; - 95C2B0F50F296545000D3E5D /* items */ = { - isa = PBXGroup; - children = ( - 95C2B0F60F296545000D3E5D /* attachment.cpp */, - 95C2B0F70F296545000D3E5D /* attachment.hpp */, - 95C2B0F80F296545000D3E5D /* attachment_manager.cpp */, - 95C2B0F90F296545000D3E5D /* attachment_manager.hpp */, - 95C2B0FA0F296545000D3E5D /* bowling.cpp */, - 95C2B0FB0F296545000D3E5D /* bowling.hpp */, - 95C2B0FE0F296545000D3E5D /* cake.cpp */, - 95C2B0FF0F296545000D3E5D /* cake.hpp */, - 95C2B1000F296545000D3E5D /* flyable.cpp */, - 95C2B1010F296545000D3E5D /* flyable.hpp */, - 95C2B1020F296545000D3E5D /* item.cpp */, - 95C2B1030F296545000D3E5D /* item.hpp */, - 95C2B1040F296545000D3E5D /* item_manager.cpp */, - 95C2B1050F296545000D3E5D /* item_manager.hpp */, - 95C2B1060F296545000D3E5D /* plunger.cpp */, - 95C2B1070F296545000D3E5D /* plunger.hpp */, - 95C2B1080F296545000D3E5D /* powerup.cpp */, - 95C2B1090F296545000D3E5D /* powerup.hpp */, - 95C2B10A0F296545000D3E5D /* powerup_manager.cpp */, - 95C2B10B0F296545000D3E5D /* powerup_manager.hpp */, - 95C2B10C0F296545000D3E5D /* projectile_manager.cpp */, - 95C2B10D0F296545000D3E5D /* projectile_manager.hpp */, - 95D2C42E13D6605600E84032 /* rubber_ball.cpp */, - 95D2C42F13D6605600E84032 /* rubber_ball.hpp */, - 95C2B10E0F296545000D3E5D /* rubber_band.cpp */, - 95C2B10F0F296545000D3E5D /* rubber_band.hpp */, - 959DE0C413C297B90068ED78 /* swatter.cpp */, - 959DE0C513C297B90068ED78 /* swatter.hpp */, - ); - name = items; - path = ../../items; - sourceTree = SOURCE_ROOT; - }; - 95C2B1160F296545000D3E5D /* karts */ = { - isa = PBXGroup; - children = ( - 956C6EC71128D3FB004336C8 /* controller */, - 95D69A7215226E4700D598D8 /* abstract_kart.cpp */, - 95D69A7315226E4700D598D8 /* abstract_kart.hpp */, - 952997F315B206D90028301A /* abstract_kart_animation.cpp */, - 952997F415B206D90028301A /* abstract_kart_animation.hpp */, - 952997F515B206D90028301A /* cannon_animation.cpp */, - 952997F615B206D90028301A /* cannon_animation.hpp */, - 952997F715B206D90028301A /* explosion_animation.cpp */, - 952997F815B206D90028301A /* explosion_animation.hpp */, - 95A1966014FC422D0074B892 /* ghost_kart.cpp */, - 95A1966114FC422D0074B892 /* ghost_kart.hpp */, - 95C2B1180F296545000D3E5D /* kart.cpp */, - 95C2B1190F296545000D3E5D /* kart.hpp */, - 9543D58D14D36EE3000B0888 /* kart_gfx.cpp */, - 9543D58E14D36EE3000B0888 /* kart_gfx.hpp */, - 95C2B11B0F296545000D3E5D /* kart_model.cpp */, - 95C2B11C0F296545000D3E5D /* kart_model.hpp */, - 95C2B11D0F296545000D3E5D /* kart_properties.cpp */, - 95C2B11E0F296545000D3E5D /* kart_properties.hpp */, - 95C2B11F0F296545000D3E5D /* kart_properties_manager.cpp */, - 95C2B1200F296545000D3E5D /* kart_properties_manager.hpp */, - 95A0BA2213E63F6700620EA6 /* kart_with_stats.cpp */, - 95A0BA2313E63F6700620EA6 /* kart_with_stats.hpp */, - 95BF1E66127513A100F78AE7 /* max_speed.cpp */, - 95BF1E67127513A100F78AE7 /* max_speed.hpp */, - 95C2B1210F296545000D3E5D /* moveable.cpp */, - 95C2B1220F296545000D3E5D /* moveable.hpp */, - 952997F915B206D90028301A /* rescue_animation.cpp */, - 952997FA15B206D90028301A /* rescue_animation.hpp */, - 95A197931502E5320074B892 /* skidding.cpp */, - 95A197941502E5320074B892 /* skidding.hpp */, - 95EF1E081506D6CF0041AC79 /* skidding_properties.cpp */, - 95EF1E091506D6CF0041AC79 /* skidding_properties.hpp */, - ); - name = karts; - path = ../../karts; - sourceTree = SOURCE_ROOT; - }; - 95C2B1470F296545000D3E5D /* modes */ = { - isa = PBXGroup; - children = ( - 95B0E8B316A4DC9E0037391C /* easter_egg_hunt.cpp */, - 95B0E8B416A4DC9E0037391C /* easter_egg_hunt.hpp */, - 95B0E8B516A4DC9E0037391C /* tutorial_world.cpp */, - 95B0E8B616A4DC9E0037391C /* tutorial_world.hpp */, - 952997BF15B205DE0028301A /* cutscene_world.cpp */, - 952997C015B205DE0028301A /* cutscene_world.hpp */, - 952997C115B205DE0028301A /* demo_world.cpp */, - 952997C215B205DE0028301A /* demo_world.hpp */, - 952997C315B205DE0028301A /* game_tutorial.cpp */, - 952997C415B205DE0028301A /* game_tutorial.hpp */, - 95C2B14A0F296545000D3E5D /* follow_the_leader.cpp */, - 95C2B14B0F296545000D3E5D /* follow_the_leader.hpp */, - 95C2B14C0F296545000D3E5D /* linear_world.cpp */, - 95C2B14D0F296545000D3E5D /* linear_world.hpp */, - 95933669149EC9B10031FD41 /* overworld.cpp */, - 9593366A149EC9B10031FD41 /* overworld.hpp */, - 952A1552103F68D000B1895D /* profile_world.cpp */, - 952A1553103F68D000B1895D /* profile_world.hpp */, - 95C2B14E0F296545000D3E5D /* standard_race.cpp */, - 95C2B14F0F296545000D3E5D /* standard_race.hpp */, - 95C2B1500F296545000D3E5D /* three_strikes_battle.cpp */, - 95C2B1510F296545000D3E5D /* three_strikes_battle.hpp */, - 9574F17A11206881008D202E /* world_status.cpp */, - 9574F17B11206881008D202E /* world_status.hpp */, - 9552C1F91231249000347B6C /* world_with_rank.cpp */, - 9552C1FA1231249000347B6C /* world_with_rank.hpp */, - 95C2B1520F296545000D3E5D /* world.cpp */, - 95C2B1530F296545000D3E5D /* world.hpp */, - ); - name = modes; - path = ../../modes; - sourceTree = SOURCE_ROOT; - }; - 95C2B1590F296545000D3E5D /* network */ = { - isa = PBXGroup; - children = ( - 95C2B15A0F296545000D3E5D /* character_confirm_message.hpp */, - 95C2B15B0F296545000D3E5D /* character_info_message.hpp */, - 95C2B15C0F296545000D3E5D /* character_selected_message.hpp */, - 95C2B15D0F296545000D3E5D /* connect_message.cpp */, - 95C2B15E0F296545000D3E5D /* connect_message.hpp */, - 95C2B15F0F296545000D3E5D /* flyable_info.hpp */, - 95C2B1600F296545000D3E5D /* item_info.hpp */, - 95C2B1610F296545000D3E5D /* kart_control_message.cpp */, - 95C2B1620F296545000D3E5D /* kart_control_message.hpp */, - 95C2B1630F296545000D3E5D /* kart_update_message.cpp */, - 95C2B1640F296545000D3E5D /* kart_update_message.hpp */, - 95C2B1650F296545000D3E5D /* message.cpp */, - 95C2B1660F296545000D3E5D /* message.hpp */, - 95C2B1670F296545000D3E5D /* network_kart.cpp */, - 95C2B1680F296545000D3E5D /* network_kart.hpp */, - 95C2B1690F296545000D3E5D /* network_manager.cpp */, - 95C2B16A0F296545000D3E5D /* network_manager.hpp */, - 95C2B16B0F296545000D3E5D /* num_players_message.hpp */, - 95C2B16C0F296545000D3E5D /* race_info_message.cpp */, - 95C2B16D0F296545000D3E5D /* race_info_message.hpp */, - 95C2B16E0F296545000D3E5D /* race_result_ack_message.hpp */, - 95C2B16F0F296545000D3E5D /* race_result_message.cpp */, - 95C2B1700F296545000D3E5D /* race_result_message.hpp */, - 95C2B1710F296545000D3E5D /* race_start_message.hpp */, - 95C2B1720F296545000D3E5D /* race_state.cpp */, - 95C2B1730F296545000D3E5D /* race_state.hpp */, - 95C2B1740F296545000D3E5D /* remote_kart_info.hpp */, - 95C2B1750F296545000D3E5D /* world_loaded_message.hpp */, - ); - name = network; - path = ../../network; - sourceTree = SOURCE_ROOT; - }; - 95C2B17F0F296545000D3E5D /* physics */ = { - isa = PBXGroup; - children = ( - 95C2B1800F296545000D3E5D /* btKart.cpp */, - 95C2B1810F296545000D3E5D /* btKart.hpp */, - 955764E412FB67EF005CE479 /* btKartRaycast.cpp */, - 955764E512FB67EF005CE479 /* btKartRaycast.hpp */, - 95C2B1820F296545000D3E5D /* btUprightConstraint.cpp */, - 95C2B1830F296545000D3E5D /* btUprightConstraint.hpp */, - 956830DE1132EC9E00088D14 /* irr_debug_drawer.cpp */, - 956830DF1132EC9E00088D14 /* irr_debug_drawer.hpp */, - 95C2B1840F296545000D3E5D /* kart_motion_state.hpp */, - 95CA59F70F82FCB7003323DB /* physical_object.cpp */, - 95CA59F60F82FCB7003323DB /* physical_object.hpp */, - 95C2B1870F296545000D3E5D /* physics.cpp */, - 95C2B1880F296545000D3E5D /* physics.hpp */, - 953EAAB10F30A4410000D57D /* triangle_mesh.cpp */, - 953EAAB00F30A4410000D57D /* triangle_mesh.hpp */, - ); - name = physics; - path = ../../physics; - sourceTree = SOURCE_ROOT; - }; - 95C2B1CE0F296545000D3E5D /* tracks */ = { - isa = PBXGroup; - children = ( - 9529980A15B207450028301A /* check_cannon.cpp */, - 9529980B15B207450028301A /* check_cannon.hpp */, - 95C77D621069589B0080838E /* ambient_light_sphere.cpp */, - 95C77D611069589B0080838E /* ambient_light_sphere.hpp */, - 95CB476B0FF30EF400413BAE /* bezier_curve.cpp */, - 95CB476A0FF30EF400413BAE /* bezier_curve.hpp */, - 95251F3B12554AB200505BA5 /* check_lap.cpp */, - 95251F3C12554AB200505BA5 /* check_lap.hpp */, - 95C77D65106958A50080838E /* check_line.cpp */, - 95C77D64106958A50080838E /* check_line.hpp */, - 955DE88110042701006A4F3C /* check_manager.cpp */, - 955DE88210042701006A4F3C /* check_manager.hpp */, - 95C77D6D106958E10080838E /* check_sphere.cpp */, - 95C77D6E106958E10080838E /* check_sphere.hpp */, - 955DE8871004273B006A4F3C /* check_structure.cpp */, - 955DE8881004273B006A4F3C /* check_structure.hpp */, - 953789720FC7829100DD1F8E /* graph_node.cpp */, - 953789710FC7829100DD1F8E /* graph_node.hpp */, - 95AA4C65148AF2CC0053771D /* lod_node_loader.cpp */, - 95AA4C66148AF2CC0053771D /* lod_node_loader.hpp */, - 951C357E0FC05BF400A48379 /* quad_set.cpp */, - 951C357D0FC05BF400A48379 /* quad_set.hpp */, - 953789810FC7831400DD1F8E /* quad.cpp */, - 953789800FC7831400DD1F8E /* quad.hpp */, - 951C35800FC05BF400A48379 /* quad_graph.cpp */, - 951C357F0FC05BF400A48379 /* quad_graph.hpp */, - 953EAAAE0F30A4220000D57D /* terrain_info.cpp */, - 953EAAAD0F30A4220000D57D /* terrain_info.hpp */, - 95C2B1CF0F296545000D3E5D /* track.cpp */, - 95C2B1D00F296545000D3E5D /* track.hpp */, - 95C2B1D10F296545000D3E5D /* track_manager.cpp */, - 95C2B1D20F296545000D3E5D /* track_manager.hpp */, - 959482CF10EBC0790031BADF /* track_object_manager.cpp */, - 959482D010EBC0790031BADF /* track_object_manager.hpp */, - 959482D110EBC0790031BADF /* track_object.cpp */, - 959482D210EBC0790031BADF /* track_object.hpp */, - 958806E713EB675F005F90FE /* track_sector.cpp */, - 958806E813EB675F005F90FE /* track_sector.hpp */, - ); - name = tracks; - path = ../../tracks; - sourceTree = SOURCE_ROOT; - }; - 95C2B1DF0F296546000D3E5D /* utils */ = { - isa = PBXGroup; - children = ( - 95B0E8A616A4DC060037391C /* log.cpp */, - 95B0E8A716A4DC060037391C /* log.hpp */, - 9553823910FD4FEC00737979 /* constants.cpp */, - 95C2B1E00F296546000D3E5D /* constants.hpp */, - 95C631A3142AC79500416D47 /* leak_check.cpp */, - 95C631B3142AC95500416D47 /* leak_check.hpp */, - 9540E2570FD5F8FD002985B8 /* no_copy.hpp */, - 957899B613C8E05F007AA5A3 /* profiler.cpp */, - 957899B713C8E05F007AA5A3 /* profiler.hpp */, - 9540E2560FD5F8FD002985B8 /* ptr_vector.hpp */, - 95C2B1E20F296546000D3E5D /* random_generator.cpp */, - 95C2B1E30F296546000D3E5D /* random_generator.hpp */, - 95C2B1E60F296546000D3E5D /* string_utils.cpp */, - 95C2B1E70F296546000D3E5D /* string_utils.hpp */, - 95C87F2D133A34E400D86528 /* time.hpp */, - 953EAAB50F30A4650000D57D /* translation.cpp */, - 953EAAB40F30A4650000D57D /* translation.hpp */, - 95C87F2E133A34EE00D86528 /* utf8 */, - 95C87F32133A34EE00D86528 /* utf8.h */, - 95C2B1E80F296546000D3E5D /* vec3.cpp */, - 95C2B1E90F296546000D3E5D /* vec3.hpp */, - ); - name = utils; - path = ../../utils; - sourceTree = SOURCE_ROOT; - }; - 95C65D750F532F7D00BE7BA7 /* io */ = { - isa = PBXGroup; - children = ( - 9505570E0F6963790056E88C /* file_manager.cpp */, - 9505570F0F6963790056E88C /* file_manager.hpp */, - 95C65D760F532F7D00BE7BA7 /* xml_node.cpp */, - 95C65D770F532F7D00BE7BA7 /* xml_node.hpp */, - 951B50AD12C9698B004F6993 /* xml_writer.cpp */, - 951B50AF12C96A13004F6993 /* xml_writer.hpp */, - ); - name = io; - path = ../../io; - sourceTree = SOURCE_ROOT; - }; - 95C87F2E133A34EE00D86528 /* utf8 */ = { - isa = PBXGroup; - children = ( - 95C87F2F133A34EE00D86528 /* checked.h */, - 95C87F30133A34EE00D86528 /* core.h */, - 95C87F31133A34EE00D86528 /* unchecked.h */, - ); - name = utf8; - path = ../../utils/utf8; - sourceTree = SOURCE_ROOT; - }; - 95D71F6316BF380900C0F691 /* wiiuse */ = { - isa = PBXGroup; - children = ( - 95D71F6416BF380900C0F691 /* classic.c */, - 95D71F6516BF380900C0F691 /* classic.h */, - 95D71F6616BF380900C0F691 /* CMakeLists.txt */, - 95D71F6716BF380900C0F691 /* definitions.h */, - 95D71F6816BF380900C0F691 /* definitions_os.h */, - 95D71F6916BF380900C0F691 /* dynamics.c */, - 95D71F6A16BF380900C0F691 /* dynamics.h */, - 95D71F6B16BF380900C0F691 /* events.c */, - 95D71F6C16BF380900C0F691 /* events.h */, - 95D71F6D16BF380900C0F691 /* guitar_hero_3.c */, - 95D71F6E16BF380900C0F691 /* guitar_hero_3.h */, - 95D71F6F16BF380900C0F691 /* io.c */, - 95D71F7016BF380900C0F691 /* io.h */, - 95D71F7216BF380900C0F691 /* io_win.c */, - 95D71F7316BF380900C0F691 /* ir.c */, - 95D71F7416BF380900C0F691 /* ir.h */, - 95D71F7516BF380900C0F691 /* motion_plus.c */, - 95D71F7616BF380900C0F691 /* motion_plus.h */, - 95D71F7716BF380900C0F691 /* nunchuk.c */, - 95D71F7816BF380900C0F691 /* nunchuk.h */, - 95D71F7916BF380900C0F691 /* os.h */, - 95D71F7A16BF380900C0F691 /* os_mac */, - 95D71F7F16BF380900C0F691 /* os_nix.c */, - 95D71F8016BF380900C0F691 /* os_win.c */, - 95D71F8116BF380900C0F691 /* README */, - 95D71F8216BF380900C0F691 /* util.c */, - 95D71F8316BF380900C0F691 /* wiiboard.c */, - 95D71F8416BF380900C0F691 /* wiiboard.h */, - 95D71F8516BF380900C0F691 /* wiiuse */, - 95D71F8716BF380900C0F691 /* wiiuse.c */, - 95D71F8816BF380900C0F691 /* wiiuse.h */, - 95D71F8916BF380900C0F691 /* wiiuse_internal.h */, - 95D71F8A16BF380900C0F691 /* wiiuse_msvcstdint.h */, - ); - name = wiiuse; - path = ../../../lib/wiiuse; - sourceTree = SOURCE_ROOT; - }; - 95D71F7A16BF380900C0F691 /* os_mac */ = { - isa = PBXGroup; - children = ( - 95D71F7B16BF380900C0F691 /* os_mac.h */, - 95D71F7C16BF380900C0F691 /* os_mac.m */, - 95D71F7D16BF380900C0F691 /* os_mac_find.m */, - 95D71F7E16BF380900C0F691 /* os_mac_interface.m */, - ); - name = os_mac; - path = ../../../lib/wiiuse/os_mac; - sourceTree = SOURCE_ROOT; - }; - 95D71F8516BF380900C0F691 /* wiiuse */ = { - isa = PBXGroup; - children = ( - 95D71F8616BF380900C0F691 /* wiiuse.vcproj */, - ); - name = wiiuse; - path = ../../../lib/wiiuse/wiiuse; - sourceTree = SOURCE_ROOT; - }; - 95D950CC0FE473CA002E10AD /* config */ = { - isa = PBXGroup; - children = ( - 9529980615B2072E0028301A /* player.cpp */, - 95B5CD13102DE08F00EF2001 /* device_config.cpp */, - 95B5CD12102DE08F00EF2001 /* device_config.hpp */, - 95D950CD0FE473CA002E10AD /* player.hpp */, - 95D950CE0FE473CA002E10AD /* stk_config.cpp */, - 95D950CF0FE473CA002E10AD /* stk_config.hpp */, - 95D950D00FE473CA002E10AD /* user_config.cpp */, - 95D950D10FE473CA002E10AD /* user_config.hpp */, - ); - name = config; - path = ../../config; - sourceTree = SOURCE_ROOT; - }; - 95ECA0EB10124C5000D47C5F /* widgets */ = { - isa = PBXGroup; - children = ( - 956B0A9E1232D2E900767CCD /* bubble_widget.cpp */, - 956B0AA21232D45D00767CCD /* bubble_widget.hpp */, - 95ECA0EC10124C5000D47C5F /* button_widget.cpp */, - 95ECA0ED10124C5000D47C5F /* button_widget.hpp */, - 9575720F134F5B890037747B /* CGUIEditBox.cpp */, - 95757210134F5B890037747B /* CGUIEditBox.h */, - 95ECA0EE10124C5000D47C5F /* check_box_widget.cpp */, - 95ECA0EF10124C5000D47C5F /* check_box_widget.hpp */, - 9524739510497C75000C197E /* dynamic_ribbon_widget.cpp */, - 9524739410497C75000C197E /* dynamic_ribbon_widget.hpp */, - 95ECA0F010124C5000D47C5F /* icon_button_widget.cpp */, - 95ECA0F110124C5000D47C5F /* icon_button_widget.hpp */, - 95ECA0F210124C5000D47C5F /* label_widget.cpp */, - 95ECA0F310124C5000D47C5F /* label_widget.hpp */, - 95ECA0F410124C5000D47C5F /* list_widget.cpp */, - 95ECA0F510124C5000D47C5F /* list_widget.hpp */, - 95ECA0F610124C5000D47C5F /* model_view_widget.cpp */, - 95ECA0F710124C5000D47C5F /* model_view_widget.hpp */, - 9545ABC811E3E38300D3C37A /* progress_bar_widget.cpp */, - 9545ABC911E3E38300D3C37A /* progress_bar_widget.hpp */, - 95ECA0FA10124C5000D47C5F /* ribbon_widget.cpp */, - 95ECA0FB10124C5000D47C5F /* ribbon_widget.hpp */, - 95ECA0FC10124C5000D47C5F /* spinner_widget.cpp */, - 95ECA0FD10124C5000D47C5F /* spinner_widget.hpp */, - 95ECA0FE10124C5000D47C5F /* text_box_widget.cpp */, - 95ECA0FF10124C5000D47C5F /* text_box_widget.hpp */, - ); - name = widgets; - path = ../../guiengine/widgets; - sourceTree = SOURCE_ROOT; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 9551C8200FC1B6FF00DB481B /* SuperTuxKart */ = { - isa = PBXNativeTarget; - buildConfigurationList = 9551C8260FC1B70000DB481B /* Build configuration list for PBXNativeTarget "SuperTuxKart" */; - buildPhases = ( - 9551C81D0FC1B6FF00DB481B /* Resources */, - 9551C81E0FC1B6FF00DB481B /* Sources */, - 9551C81F0FC1B6FF00DB481B /* Frameworks */, - 9507E9C60FC1CD1C00BD2B92 /* Copy data files */, - 9507E9E70FC1CDDF00BD2B92 /* Copy frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SuperTuxKart; - productName = "SuperTuxKart - distribution"; - productReference = 9551C8210FC1B6FF00DB481B /* SuperTuxKart.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "STK_XCode" */; - compatibilityVersion = "Xcode 2.4"; - hasScannedForEncodings = 1; - knownRegions = ( - English, - Japanese, - French, - German, - ); - mainGroup = 08FB7794FE84155DC02AAC07 /* STK_XCode */; - projectDirPath = ""; - projectRoot = ../../..; - targets = ( - 9551C8200FC1B6FF00DB481B /* SuperTuxKart */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 9551C81D0FC1B6FF00DB481B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 9507E9B70FC1CCE900BD2B92 /* stk.icns in Resources */, - 9593A2CB1609386100AB5EEB /* Makefile.am in Resources */, - 9593A2F91609388C00AB5EEB /* aclocal.m4 in Resources */, - 9593A2FB1609388C00AB5EEB /* ChangeLog in Resources */, - 9593A2FC1609388C00AB5EEB /* CMakeLists.txt in Resources */, - 9593A2FE1609388C00AB5EEB /* configure in Resources */, - 9593A2FF1609388C00AB5EEB /* configure.in in Resources */, - 9593A3001609388C00AB5EEB /* depcomp in Resources */, - 9593A3011609388C00AB5EEB /* design.txt in Resources */, - 9593A3081609388C00AB5EEB /* Doxyfile in Resources */, - 9593A3091609388C00AB5EEB /* enet.dsp in Resources */, - 9593A30B1609388C00AB5EEB /* install-sh in Resources */, - 9593A30C1609388C00AB5EEB /* LICENSE in Resources */, - 9593A30E1609388C00AB5EEB /* Makefile.am in Resources */, - 9593A30F1609388C00AB5EEB /* missing in Resources */, - 9593A3131609388C00AB5EEB /* README in Resources */, - 9593A3141609388C00AB5EEB /* tutorial.txt in Resources */, - 95D71F8C16BF380900C0F691 /* CMakeLists.txt in Resources */, - 95D71F9B16BF380900C0F691 /* README in Resources */, - 95D71F9E16BF380900C0F691 /* wiiuse.vcproj in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 9507E9C60FC1CD1C00BD2B92 /* Copy data files */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 12; - files = ( - ); - inputPaths = ( - ); - name = "Copy data files"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# copy data files\n\necho \"Copying Data Files\"\n\nif [ \"${CONFIGURATION}\" = \"Release\" ]\nthen\n\nmkdir -p \"${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Resources/data\"\ncp -rf \"${PROJECT_DIR}/../../../data\" \"${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Resources/\"\n\"${PROJECT_DIR}/cleanupRelease.sh\"\n\nelse\n\nmkdir -p \"${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Resources/\"\nln -sFhf \"${PROJECT_DIR}/../../../data\" \"${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Resources/\"\n\nfi"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 9551C81E0FC1B6FF00DB481B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 9551C8270FC1B72500DB481B /* music_information.cpp in Sources */, - 9551C8280FC1B72500DB481B /* music_ogg.cpp in Sources */, - 9551C8290FC1B72500DB481B /* sfx_manager.cpp in Sources */, - 9551C82A0FC1B72500DB481B /* sfx_openal.cpp in Sources */, - 9551C8900FC1B72500DB481B /* challenge.cpp in Sources */, - 9551C8910FC1B72500DB481B /* challenge_data.cpp in Sources */, - 9551C8920FC1B72500DB481B /* unlock_manager.cpp in Sources */, - 9551C8A20FC1B72500DB481B /* attachment.cpp in Sources */, - 9551C8A30FC1B72500DB481B /* attachment_manager.cpp in Sources */, - 9551C8A40FC1B72500DB481B /* bowling.cpp in Sources */, - 9551C8A60FC1B72500DB481B /* cake.cpp in Sources */, - 9551C8A70FC1B72500DB481B /* flyable.cpp in Sources */, - 9551C8A80FC1B72500DB481B /* item.cpp in Sources */, - 9551C8A90FC1B72500DB481B /* item_manager.cpp in Sources */, - 9551C8AA0FC1B72500DB481B /* plunger.cpp in Sources */, - 9551C8AB0FC1B72500DB481B /* powerup.cpp in Sources */, - 9551C8AC0FC1B72500DB481B /* powerup_manager.cpp in Sources */, - 9551C8AD0FC1B72500DB481B /* projectile_manager.cpp in Sources */, - 9551C8AE0FC1B72500DB481B /* rubber_band.cpp in Sources */, - 9551C8AF0FC1B72500DB481B /* kart.cpp in Sources */, - 9551C8B00FC1B72500DB481B /* kart_model.cpp in Sources */, - 9551C8B10FC1B72500DB481B /* kart_properties.cpp in Sources */, - 9551C8B20FC1B72500DB481B /* kart_properties_manager.cpp in Sources */, - 9551C8B30FC1B72500DB481B /* moveable.cpp in Sources */, - 9551C8B90FC1B72500DB481B /* main.cpp in Sources */, - 9551C8BA0FC1B72500DB481B /* main_loop.cpp in Sources */, - 9551C8BE0FC1B72500DB481B /* follow_the_leader.cpp in Sources */, - 9551C8BF0FC1B72500DB481B /* linear_world.cpp in Sources */, - 9551C8C00FC1B72500DB481B /* standard_race.cpp in Sources */, - 9551C8C10FC1B72500DB481B /* three_strikes_battle.cpp in Sources */, - 9551C8C20FC1B72500DB481B /* world.cpp in Sources */, - 9551C8C30FC1B72500DB481B /* connect_message.cpp in Sources */, - 9551C8C40FC1B72500DB481B /* kart_control_message.cpp in Sources */, - 9551C8C50FC1B72500DB481B /* kart_update_message.cpp in Sources */, - 9551C8C60FC1B72500DB481B /* message.cpp in Sources */, - 9551C8C70FC1B72500DB481B /* network_kart.cpp in Sources */, - 9551C8C80FC1B72500DB481B /* network_manager.cpp in Sources */, - 9551C8C90FC1B72500DB481B /* race_info_message.cpp in Sources */, - 9551C8CA0FC1B72500DB481B /* race_result_message.cpp in Sources */, - 9551C8CB0FC1B72500DB481B /* race_state.cpp in Sources */, - 9551C8CC0FC1B72500DB481B /* btKart.cpp in Sources */, - 9551C8CD0FC1B72500DB481B /* btUprightConstraint.cpp in Sources */, - 9551C8CE0FC1B72500DB481B /* physics.cpp in Sources */, - 9551C8D70FC1B72500DB481B /* track.cpp in Sources */, - 9551C8D80FC1B72500DB481B /* track_manager.cpp in Sources */, - 9551C8DA0FC1B72500DB481B /* random_generator.cpp in Sources */, - 9551C8DC0FC1B72500DB481B /* string_utils.cpp in Sources */, - 9551C8DD0FC1B72500DB481B /* vec3.cpp in Sources */, - 9551C8DE0FC1B72500DB481B /* terrain_info.cpp in Sources */, - 9551C8DF0FC1B72500DB481B /* triangle_mesh.cpp in Sources */, - 9551C8E00FC1B72500DB481B /* translation.cpp in Sources */, - 9551C8E10FC1B72500DB481B /* xml_node.cpp in Sources */, - 9551C8EC0FC1B72500DB481B /* file_manager.cpp in Sources */, - 9551C8F60FC1B72500DB481B /* input_manager.cpp in Sources */, - 9551C8F70FC1B72500DB481B /* input_device.cpp in Sources */, - 9551C8F80FC1B72500DB481B /* device_manager.cpp in Sources */, - 9551C8F90FC1B72500DB481B /* physical_object.cpp in Sources */, - 9551C8FA0FC1B72500DB481B /* quad_set.cpp in Sources */, - 9551C8FB0FC1B72500DB481B /* quad_graph.cpp in Sources */, - 953789730FC7829100DD1F8E /* graph_node.cpp in Sources */, - 953789820FC7831400DD1F8E /* quad.cpp in Sources */, - 95263DEC0FD7471900CF5F92 /* grand_prix_data.cpp in Sources */, - 95263DED0FD7471900CF5F92 /* grand_prix_manager.cpp in Sources */, - 95263DEE0FD7471900CF5F92 /* highscore_manager.cpp in Sources */, - 95263DEF0FD7471900CF5F92 /* highscores.cpp in Sources */, - 95263DF00FD7471900CF5F92 /* history.cpp in Sources */, - 95263DF10FD7471900CF5F92 /* race_manager.cpp in Sources */, - 95D950D20FE473CA002E10AD /* stk_config.cpp in Sources */, - 95D950D30FE473CA002E10AD /* user_config.cpp in Sources */, - 95CB476C0FF30EF400413BAE /* bezier_curve.cpp in Sources */, - 954E4C2D0FF98B6F0047FE3E /* animation_base.cpp in Sources */, - 954E4C2F0FF98B6F0047FE3E /* billboard_animation.cpp in Sources */, - 954E4C300FF98B6F0047FE3E /* three_d_animation.cpp in Sources */, - 951BC65E0FFAF290006B5FF1 /* ipo.cpp in Sources */, - 955DE88310042701006A4F3C /* check_manager.cpp in Sources */, - 955DE88C1004273B006A4F3C /* check_structure.cpp in Sources */, - 958330CC10122B4A00C5137E /* engine.cpp in Sources */, - 958330CD10122B4A00C5137E /* event_handler.cpp in Sources */, - 958330CE10122B4A00C5137E /* modaldialog.cpp in Sources */, - 958330D010122B4A00C5137E /* screen.cpp in Sources */, - 958330D110122B4A00C5137E /* screen_loader.cpp in Sources */, - 958330D210122B4A00C5137E /* skin.cpp in Sources */, - 958330D310122B4A00C5137E /* widget.cpp in Sources */, - 958330D410122B4A00C5137E /* credits.cpp in Sources */, - 958330D510122B4A00C5137E /* kart_selection.cpp in Sources */, - 958330D710122B4A00C5137E /* race_gui.cpp in Sources */, - 958330D810122B4A00C5137E /* state_manager.cpp in Sources */, - 9583319910123B0200C5137E /* abstract_state_manager.cpp in Sources */, - 9583323F101243ED00C5137E /* enter_player_name_dialog.cpp in Sources */, - 95833240101243ED00C5137E /* player_info_dialog.cpp in Sources */, - 95833241101243ED00C5137E /* press_a_key_dialog.cpp in Sources */, - 95833242101243ED00C5137E /* track_info_dialog.cpp in Sources */, - 95ECA10010124C5000D47C5F /* button_widget.cpp in Sources */, - 95ECA10110124C5000D47C5F /* check_box_widget.cpp in Sources */, - 95ECA10210124C5000D47C5F /* icon_button_widget.cpp in Sources */, - 95ECA10310124C5000D47C5F /* label_widget.cpp in Sources */, - 95ECA10410124C5000D47C5F /* list_widget.cpp in Sources */, - 95ECA10510124C5000D47C5F /* model_view_widget.cpp in Sources */, - 95ECA10710124C5000D47C5F /* ribbon_widget.cpp in Sources */, - 95ECA10810124C5000D47C5F /* spinner_widget.cpp in Sources */, - 95ECA10910124C5000D47C5F /* text_box_widget.cpp in Sources */, - 95B5CD14102DE08F00EF2001 /* device_config.cpp in Sources */, - 952A1545103F66D600B1895D /* camera.cpp in Sources */, - 952A1546103F66D600B1895D /* explosion.cpp in Sources */, - 952A1547103F66D600B1895D /* irr_driver.cpp in Sources */, - 952A1548103F66D600B1895D /* material.cpp in Sources */, - 952A1549103F66D600B1895D /* material_manager.cpp in Sources */, - 952A154A103F66D600B1895D /* mesh_tools.cpp in Sources */, - 952A154B103F66D600B1895D /* moving_texture.cpp in Sources */, - 952A154D103F66D600B1895D /* shadow.cpp in Sources */, - 952A154E103F66D600B1895D /* skid_marks.cpp in Sources */, - 952A1554103F68D000B1895D /* profile_world.cpp in Sources */, - 9524739610497C75000C197E /* dynamic_ribbon_widget.cpp in Sources */, - 958D8C54104F523000A81934 /* race_paused_dialog.cpp in Sources */, - 95C77D631069589B0080838E /* ambient_light_sphere.cpp in Sources */, - 95C77D66106958A50080838E /* check_line.cpp in Sources */, - 95C77D6F106958E10080838E /* check_sphere.cpp in Sources */, - 95D2343F1078227A00625256 /* feature_unlocked.cpp in Sources */, - 9522F125107948AD0067ECF5 /* main_menu_screen.cpp in Sources */, - 9522F15B107949780067ECF5 /* race_setup_screen.cpp in Sources */, - 9522F15E10794A350067ECF5 /* tracks_screen.cpp in Sources */, - 9522F1E010795E8A0067ECF5 /* help_screen_1.cpp in Sources */, - 9522F1E510795EFF0067ECF5 /* help_screen_2.cpp in Sources */, - 9522F1E610795EFF0067ECF5 /* help_screen_3.cpp in Sources */, - 9522F1F0107961560067ECF5 /* options_screen_input.cpp in Sources */, - 9522F1F1107961560067ECF5 /* options_screen_players.cpp in Sources */, - 956541BB10DD5F0A00C83E99 /* arenas_screen.cpp in Sources */, - 956541E110DD628C00C83E99 /* add_device_dialog.cpp in Sources */, - 95C9C97210E93456005A418D /* stars.cpp in Sources */, - 959482D310EBC0790031BADF /* track_object_manager.cpp in Sources */, - 959482D410EBC0790031BADF /* track_object.cpp in Sources */, - 9553823A10FD4FEC00737979 /* constants.cpp in Sources */, - 95DFC5021106933B00A043A9 /* slip_stream.cpp in Sources */, - 95E246BE111A2959000C965D /* confirm_resolution_dialog.cpp in Sources */, - 9574F17C11206881008D202E /* world_status.cpp in Sources */, - 95634EF21126272C009C145D /* gp_info_dialog.cpp in Sources */, - 956C6ED31128D3FB004336C8 /* controller.cpp in Sources */, - 956C6ED51128D3FB004336C8 /* end_controller.cpp in Sources */, - 956C6ED71128D3FB004336C8 /* player_controller.cpp in Sources */, - 956830E01132EC9E00088D14 /* irr_debug_drawer.cpp in Sources */, - 957ED4801163FF18002AB42C /* ai_base_controller.cpp in Sources */, - 958BD770117F6AE90095B483 /* music_manager.cpp in Sources */, - 950D448C118DEE3C006CFC41 /* CGUISpriteBank.cpp in Sources */, - 950D45D1118E040E006CFC41 /* options_screen_input2.cpp in Sources */, - 9556A880119EF976009C558F /* options_screen_audio.cpp in Sources */, - 9556A881119EF976009C558F /* options_screen_video.cpp in Sources */, - 9586318411B1EC9F00B8B4AF /* grand_prix_lose.cpp in Sources */, - 9586318511B1EC9F00B8B4AF /* grand_prix_win.cpp in Sources */, - 9525B71411C851D30094BD96 /* CBatchingMesh.cpp in Sources */, - 9551B27111DC0D4D002DD140 /* addons_screen.cpp in Sources */, - 9551B27C11DC0D6A002DD140 /* zip.cpp in Sources */, - 95E6A0C011DCF37800AE088A /* addons_loading.cpp in Sources */, - 9545ABCA11E3E38300D3C37A /* progress_bar_widget.cpp in Sources */, - 9554C4A611F1188000906416 /* race_result_gui.cpp in Sources */, - 953F8B2111F7C13C00205E66 /* scalable_font.cpp in Sources */, - 9560368C12187EFB00EB96C4 /* layout_manager.cpp in Sources */, - 956039BA1218C09E00EB96C4 /* abstract_top_level_container.cpp in Sources */, - 9552C1FB1231249000347B6C /* world_with_rank.cpp in Sources */, - 956B0A9F1232D2E900767CCD /* bubble_widget.cpp in Sources */, - 95017B41124698C400C90D56 /* help_screen_4.cpp in Sources */, - 95251F3D12554AB200505BA5 /* check_lap.cpp in Sources */, - 9516B07E12629C4E005F9493 /* sfx_buffer.cpp in Sources */, - 95BF1E68127513A100F78AE7 /* max_speed.cpp in Sources */, - 9528CC241291E7A10078A5EF /* binding.cpp in Sources */, - 95395A77129DFE130079BCE7 /* message_dialog.cpp in Sources */, - 9538E2B912C25D6800172896 /* addons_manager.cpp in Sources */, - 9538E2BA12C25D6800172896 /* network_http.cpp in Sources */, - 951B50AE12C9698B004F6993 /* xml_writer.cpp in Sources */, - 9538A56012CD094200CE3220 /* addon.cpp in Sources */, - 9542FC7712D3BDB000C00366 /* particle_emitter.cpp in Sources */, - 9542FD4C12D3E0D700C00366 /* particle_kind.cpp in Sources */, - 9528C71612D69494006E9167 /* particle_kind_manager.cpp in Sources */, - 955764E612FB67EF005CE479 /* btKartRaycast.cpp in Sources */, - 9559DE7F12FF777600350DE8 /* rain.cpp in Sources */, - 9592DC6D13021B350039DBC8 /* minimal_race_gui.cpp in Sources */, - 95E1FCDF130369EB004D83CC /* per_camera_node.cpp in Sources */, - 95376CAF1320784100C842A4 /* lod_node.cpp in Sources */, - 9584449E1330F89100CEA60A /* dictionary.cpp in Sources */, - 9584449F1330F89100CEA60A /* dictionary_manager.cpp in Sources */, - 958444A01330F89100CEA60A /* iconv.cpp in Sources */, - 958444A11330F89100CEA60A /* language.cpp in Sources */, - 958444A31330F89100CEA60A /* plural_forms.cpp in Sources */, - 958444A41330F89100CEA60A /* po_parser.cpp in Sources */, - 958444A51330F89100CEA60A /* stk_file_system.cpp in Sources */, - 958444A61330F89100CEA60A /* tinygettext.cpp in Sources */, - 953A959D13367D6E00D86B4D /* options_screen_ui.cpp in Sources */, - 95757211134F5B890037747B /* CGUIEditBox.cpp in Sources */, - 956F557513527E4B005C291D /* race_gui_base.cpp in Sources */, - 9584B30D137CAC12008565D7 /* news_manager.cpp in Sources */, - 9584B30E137CAC12008565D7 /* request.cpp in Sources */, - 959DE0C613C297B90068ED78 /* swatter.cpp in Sources */, - 957899B813C8E05F007AA5A3 /* profiler.cpp in Sources */, - 9598A5B613CFBA83000B83EA /* hardware_skinning.cpp in Sources */, - 95D2C43013D6605600E84032 /* rubber_ball.cpp in Sources */, - 958D924213DA48A70097BC82 /* post_processing.cpp in Sources */, - 95A0BA2413E63F6700620EA6 /* kart_with_stats.cpp in Sources */, - 958806E913EB675F005F90FE /* track_sector.cpp in Sources */, - 957A2D291405E21D00F45B22 /* hit_sfx.cpp in Sources */, - 957817DC142142C500AD07B2 /* referee.cpp in Sources */, - 95C631A4142AC79500416D47 /* leak_check.cpp in Sources */, - 95AA4C67148AF2CC0053771D /* lod_node_loader.cpp in Sources */, - 95E5C318148C17E500AD3FCC /* story_mode_lobby.cpp in Sources */, - 95E5C335148C19F500AD3FCC /* story_mode_new.cpp in Sources */, - 95E5C3C2148C418100AD3FCC /* game_slot.cpp in Sources */, - 9593366B149EC9B10031FD41 /* overworld.cpp in Sources */, - 957957A214A3CA3900E72497 /* custom_video_settings.cpp in Sources */, - 95EF178E14AFBC91005FFEEB /* race_gui_overworld.cpp in Sources */, - 9543D58F14D36EE3000B0888 /* kart_gfx.cpp in Sources */, - 9543D59E14D38831000B0888 /* select_challenge.cpp in Sources */, - 95A1966214FC422D0074B892 /* ghost_kart.cpp in Sources */, - 95A1978E1502E5020074B892 /* replay_base.cpp in Sources */, - 95A1978F1502E5020074B892 /* replay_play.cpp in Sources */, - 95A197901502E5020074B892 /* replay_recorder.cpp in Sources */, - 95A197951502E5320074B892 /* skidding.cpp in Sources */, - 95EF1E0A1506D6CF0041AC79 /* skidding_properties.cpp in Sources */, - 95D69A7415226E4700D598D8 /* abstract_kart.cpp in Sources */, - 952997C715B205DE0028301A /* cutscene_world.cpp in Sources */, - 952997C815B205DE0028301A /* demo_world.cpp in Sources */, - 952997C915B205DE0028301A /* game_tutorial.cpp in Sources */, - 952997ED15B206890028301A /* skidding_ai.cpp in Sources */, - 952997FB15B206D90028301A /* abstract_kart_animation.cpp in Sources */, - 952997FC15B206D90028301A /* cannon_animation.cpp in Sources */, - 952997FD15B206D90028301A /* explosion_animation.cpp in Sources */, - 952997FE15B206D90028301A /* rescue_animation.cpp in Sources */, - 9529980315B2070C0028301A /* cutscene_gui.cpp in Sources */, - 9529980715B2072E0028301A /* player.cpp in Sources */, - 9529980C15B207450028301A /* check_cannon.cpp in Sources */, - 9529981115B207740028301A /* show_curve.cpp in Sources */, - 9593A2551609386100AB5EEB /* btAxisSweep3.cpp in Sources */, - 9593A2561609386100AB5EEB /* btBroadphaseProxy.cpp in Sources */, - 9593A2571609386100AB5EEB /* btCollisionAlgorithm.cpp in Sources */, - 9593A2581609386100AB5EEB /* btDbvt.cpp in Sources */, - 9593A2591609386100AB5EEB /* btDbvtBroadphase.cpp in Sources */, - 9593A25A1609386100AB5EEB /* btDispatcher.cpp in Sources */, - 9593A25B1609386100AB5EEB /* btMultiSapBroadphase.cpp in Sources */, - 9593A25C1609386100AB5EEB /* btOverlappingPairCache.cpp in Sources */, - 9593A25D1609386100AB5EEB /* btQuantizedBvh.cpp in Sources */, - 9593A25E1609386100AB5EEB /* btSimpleBroadphase.cpp in Sources */, - 9593A25F1609386100AB5EEB /* btActivatingCollisionAlgorithm.cpp in Sources */, - 9593A2601609386100AB5EEB /* btBox2dBox2dCollisionAlgorithm.cpp in Sources */, - 9593A2611609386100AB5EEB /* btBoxBoxCollisionAlgorithm.cpp in Sources */, - 9593A2621609386100AB5EEB /* btBoxBoxDetector.cpp in Sources */, - 9593A2631609386100AB5EEB /* btCollisionDispatcher.cpp in Sources */, - 9593A2641609386100AB5EEB /* btCollisionObject.cpp in Sources */, - 9593A2651609386100AB5EEB /* btCollisionWorld.cpp in Sources */, - 9593A2661609386100AB5EEB /* btCompoundCollisionAlgorithm.cpp in Sources */, - 9593A2671609386100AB5EEB /* btConvex2dConvex2dAlgorithm.cpp in Sources */, - 9593A2681609386100AB5EEB /* btConvexConcaveCollisionAlgorithm.cpp in Sources */, - 9593A2691609386100AB5EEB /* btConvexConvexAlgorithm.cpp in Sources */, - 9593A26A1609386100AB5EEB /* btConvexPlaneCollisionAlgorithm.cpp in Sources */, - 9593A26B1609386100AB5EEB /* btDefaultCollisionConfiguration.cpp in Sources */, - 9593A26C1609386100AB5EEB /* btEmptyCollisionAlgorithm.cpp in Sources */, - 9593A26D1609386100AB5EEB /* btGhostObject.cpp in Sources */, - 9593A26E1609386100AB5EEB /* btInternalEdgeUtility.cpp in Sources */, - 9593A26F1609386100AB5EEB /* btManifoldResult.cpp in Sources */, - 9593A2701609386100AB5EEB /* btSimulationIslandManager.cpp in Sources */, - 9593A2711609386100AB5EEB /* btSphereBoxCollisionAlgorithm.cpp in Sources */, - 9593A2721609386100AB5EEB /* btSphereSphereCollisionAlgorithm.cpp in Sources */, - 9593A2731609386100AB5EEB /* btSphereTriangleCollisionAlgorithm.cpp in Sources */, - 9593A2741609386100AB5EEB /* btUnionFind.cpp in Sources */, - 9593A2751609386100AB5EEB /* SphereTriangleDetector.cpp in Sources */, - 9593A2761609386100AB5EEB /* btBox2dShape.cpp in Sources */, - 9593A2771609386100AB5EEB /* btBoxShape.cpp in Sources */, - 9593A2781609386100AB5EEB /* btBvhTriangleMeshShape.cpp in Sources */, - 9593A2791609386100AB5EEB /* btCapsuleShape.cpp in Sources */, - 9593A27A1609386100AB5EEB /* btCollisionShape.cpp in Sources */, - 9593A27B1609386100AB5EEB /* btCompoundShape.cpp in Sources */, - 9593A27C1609386100AB5EEB /* btConcaveShape.cpp in Sources */, - 9593A27D1609386100AB5EEB /* btConeShape.cpp in Sources */, - 9593A27E1609386100AB5EEB /* btConvex2dShape.cpp in Sources */, - 9593A27F1609386100AB5EEB /* btConvexHullShape.cpp in Sources */, - 9593A2801609386100AB5EEB /* btConvexInternalShape.cpp in Sources */, - 9593A2811609386100AB5EEB /* btConvexPointCloudShape.cpp in Sources */, - 9593A2821609386100AB5EEB /* btConvexPolyhedron.cpp in Sources */, - 9593A2831609386100AB5EEB /* btConvexShape.cpp in Sources */, - 9593A2841609386100AB5EEB /* btConvexTriangleMeshShape.cpp in Sources */, - 9593A2851609386100AB5EEB /* btCylinderShape.cpp in Sources */, - 9593A2861609386100AB5EEB /* btEmptyShape.cpp in Sources */, - 9593A2871609386100AB5EEB /* btHeightfieldTerrainShape.cpp in Sources */, - 9593A2881609386100AB5EEB /* btMinkowskiSumShape.cpp in Sources */, - 9593A2891609386100AB5EEB /* btMultimaterialTriangleMeshShape.cpp in Sources */, - 9593A28A1609386100AB5EEB /* btMultiSphereShape.cpp in Sources */, - 9593A28B1609386100AB5EEB /* btOptimizedBvh.cpp in Sources */, - 9593A28C1609386100AB5EEB /* btPolyhedralConvexShape.cpp in Sources */, - 9593A28D1609386100AB5EEB /* btScaledBvhTriangleMeshShape.cpp in Sources */, - 9593A28E1609386100AB5EEB /* btShapeHull.cpp in Sources */, - 9593A28F1609386100AB5EEB /* btSphereShape.cpp in Sources */, - 9593A2901609386100AB5EEB /* btStaticPlaneShape.cpp in Sources */, - 9593A2911609386100AB5EEB /* btStridingMeshInterface.cpp in Sources */, - 9593A2921609386100AB5EEB /* btTetrahedronShape.cpp in Sources */, - 9593A2931609386100AB5EEB /* btTriangleBuffer.cpp in Sources */, - 9593A2941609386100AB5EEB /* btTriangleCallback.cpp in Sources */, - 9593A2951609386100AB5EEB /* btTriangleIndexVertexArray.cpp in Sources */, - 9593A2961609386100AB5EEB /* btTriangleIndexVertexMaterialArray.cpp in Sources */, - 9593A2971609386100AB5EEB /* btTriangleMesh.cpp in Sources */, - 9593A2981609386100AB5EEB /* btTriangleMeshShape.cpp in Sources */, - 9593A2991609386100AB5EEB /* btUniformScalingShape.cpp in Sources */, - 9593A29A1609386100AB5EEB /* btContactProcessing.cpp in Sources */, - 9593A29B1609386100AB5EEB /* btGenericPoolAllocator.cpp in Sources */, - 9593A29C1609386100AB5EEB /* btGImpactBvh.cpp in Sources */, - 9593A29D1609386100AB5EEB /* btGImpactCollisionAlgorithm.cpp in Sources */, - 9593A29E1609386100AB5EEB /* btGImpactQuantizedBvh.cpp in Sources */, - 9593A29F1609386100AB5EEB /* btGImpactShape.cpp in Sources */, - 9593A2A01609386100AB5EEB /* btTriangleShapeEx.cpp in Sources */, - 9593A2A11609386100AB5EEB /* gim_box_set.cpp in Sources */, - 9593A2A21609386100AB5EEB /* gim_contact.cpp in Sources */, - 9593A2A31609386100AB5EEB /* gim_memory.cpp in Sources */, - 9593A2A41609386100AB5EEB /* gim_tri_collision.cpp in Sources */, - 9593A2A51609386100AB5EEB /* btContinuousConvexCollision.cpp in Sources */, - 9593A2A61609386100AB5EEB /* btConvexCast.cpp in Sources */, - 9593A2A71609386100AB5EEB /* btGjkConvexCast.cpp in Sources */, - 9593A2A81609386100AB5EEB /* btGjkEpa2.cpp in Sources */, - 9593A2A91609386100AB5EEB /* btGjkEpaPenetrationDepthSolver.cpp in Sources */, - 9593A2AA1609386100AB5EEB /* btGjkPairDetector.cpp in Sources */, - 9593A2AB1609386100AB5EEB /* btMinkowskiPenetrationDepthSolver.cpp in Sources */, - 9593A2AC1609386100AB5EEB /* btPersistentManifold.cpp in Sources */, - 9593A2AD1609386100AB5EEB /* btPolyhedralContactClipping.cpp in Sources */, - 9593A2AE1609386100AB5EEB /* btRaycastCallback.cpp in Sources */, - 9593A2AF1609386100AB5EEB /* btSubSimplexConvexCast.cpp in Sources */, - 9593A2B01609386100AB5EEB /* btVoronoiSimplexSolver.cpp in Sources */, - 9593A2B11609386100AB5EEB /* btKinematicCharacterController.cpp in Sources */, - 9593A2B21609386100AB5EEB /* btConeTwistConstraint.cpp in Sources */, - 9593A2B31609386100AB5EEB /* btContactConstraint.cpp in Sources */, - 9593A2B41609386100AB5EEB /* btGeneric6DofConstraint.cpp in Sources */, - 9593A2B51609386100AB5EEB /* btGeneric6DofSpringConstraint.cpp in Sources */, - 9593A2B61609386100AB5EEB /* btHinge2Constraint.cpp in Sources */, - 9593A2B71609386100AB5EEB /* btHingeConstraint.cpp in Sources */, - 9593A2B81609386100AB5EEB /* btPoint2PointConstraint.cpp in Sources */, - 9593A2B91609386100AB5EEB /* btSequentialImpulseConstraintSolver.cpp in Sources */, - 9593A2BA1609386100AB5EEB /* btSliderConstraint.cpp in Sources */, - 9593A2BB1609386100AB5EEB /* btSolve2LinearConstraint.cpp in Sources */, - 9593A2BC1609386100AB5EEB /* btTypedConstraint.cpp in Sources */, - 9593A2BD1609386100AB5EEB /* btUniversalConstraint.cpp in Sources */, - 9593A2BE1609386100AB5EEB /* btContinuousDynamicsWorld.cpp in Sources */, - 9593A2BF1609386100AB5EEB /* btDiscreteDynamicsWorld.cpp in Sources */, - 9593A2C01609386100AB5EEB /* btRigidBody.cpp in Sources */, - 9593A2C11609386100AB5EEB /* btSimpleDynamicsWorld.cpp in Sources */, - 9593A2C21609386100AB5EEB /* Bullet-C-API.cpp in Sources */, - 9593A2C31609386100AB5EEB /* btRaycastVehicle.cpp in Sources */, - 9593A2C41609386100AB5EEB /* btWheelInfo.cpp in Sources */, - 9593A2C51609386100AB5EEB /* btAlignedAllocator.cpp in Sources */, - 9593A2C61609386100AB5EEB /* btConvexHull.cpp in Sources */, - 9593A2C71609386100AB5EEB /* btConvexHullComputer.cpp in Sources */, - 9593A2C81609386100AB5EEB /* btGeometryUtil.cpp in Sources */, - 9593A2C91609386100AB5EEB /* btQuickprof.cpp in Sources */, - 9593A2CA1609386100AB5EEB /* btSerializer.cpp in Sources */, - 9593A2FA1609388C00AB5EEB /* callbacks.c in Sources */, - 9593A2FD1609388C00AB5EEB /* compress.c in Sources */, - 9593A30A1609388C00AB5EEB /* host.c in Sources */, - 9593A30D1609388C00AB5EEB /* list.c in Sources */, - 9593A3101609388C00AB5EEB /* packet.c in Sources */, - 9593A3111609388C00AB5EEB /* peer.c in Sources */, - 9593A3121609388C00AB5EEB /* protocol.c in Sources */, - 9593A3151609388C00AB5EEB /* unix.c in Sources */, - 9593A3161609388C00AB5EEB /* win32.c in Sources */, - 9593A32016093A7300AB5EEB /* ai_properties.cpp in Sources */, - 958949A9163F52FB00957345 /* inetwork_http.cpp in Sources */, - 95B0E8A816A4DC060037391C /* log.cpp in Sources */, - 95B0E8B016A4DC390037391C /* tgt_log.cpp in Sources */, - 95B0E8B716A4DC9E0037391C /* easter_egg_hunt.cpp in Sources */, - 95B0E8B816A4DC9E0037391C /* tutorial_world.cpp in Sources */, - 95D71F8B16BF380900C0F691 /* classic.c in Sources */, - 95D71F8D16BF380900C0F691 /* dynamics.c in Sources */, - 95D71F8E16BF380900C0F691 /* events.c in Sources */, - 95D71F8F16BF380900C0F691 /* guitar_hero_3.c in Sources */, - 95D71F9016BF380900C0F691 /* io.c in Sources */, - 95D71F9216BF380900C0F691 /* io_win.c in Sources */, - 95D71F9316BF380900C0F691 /* ir.c in Sources */, - 95D71F9416BF380900C0F691 /* motion_plus.c in Sources */, - 95D71F9516BF380900C0F691 /* nunchuk.c in Sources */, - 95D71F9616BF380900C0F691 /* os_mac.m in Sources */, - 95D71F9716BF380900C0F691 /* os_mac_find.m in Sources */, - 95D71F9816BF380900C0F691 /* os_mac_interface.m in Sources */, - 95D71F9916BF380900C0F691 /* os_nix.c in Sources */, - 95D71F9A16BF380900C0F691 /* os_win.c in Sources */, - 95D71F9C16BF380900C0F691 /* util.c in Sources */, - 95D71F9D16BF380900C0F691 /* wiiboard.c in Sources */, - 95D71F9F16BF380900C0F691 /* wiiuse.c in Sources */, - 95D71FB216BF3A9E00C0F691 /* wiimote_manager.cpp in Sources */, - 95D71FB716BF3C7800C0F691 /* tutorial_message_dialog.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 1DEB923608733DC60010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(NATIVE_ARCH)"; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - /usr/local/include, - "$(HEADER_SEARCH_PATHS_QUOTED_1)", - "$(HEADER_SEARCH_PATHS_QUOTED_2)", - ); - PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; - ZERO_LINK = NO; - }; - name = Debug; - }; - 1DEB923708733DC60010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(NATIVE_ARCH)"; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - /usr/local/include, - "$(HEADER_SEARCH_PATHS_QUOTED_1)", - "$(HEADER_SEARCH_PATHS_QUOTED_2)", - ); - PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; - ZERO_LINK = NO; - }; - name = Release; - }; - 9551C8240FC1B70000DB481B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9551C9F80FC1B87600DB481B /* Config.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_DEBUGGING_SYMBOLS = full; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_ENABLE_SYMBOL_SEPARATION = NO; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = ""; - GCC_VERSION = ""; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_MISSING_PARENTHESES = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; - GCC_WARN_SHADOW = NO; - GCC_WARN_UNUSED_FUNCTION = NO; - GCC_WARN_UNUSED_PARAMETER = NO; - HEADER_SEARCH_PATHS = "$(inherited)"; - HEADER_SEARCH_PATHS_QUOTED_1 = ""; - HEADER_SEARCH_PATHS_QUOTED_2 = ""; - INFOPLIST_FILE = "SuperTuxKart-Info.plist"; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", - "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", - ); - LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(DEVELOPER_DIR)/games/supertuxkart/src/bullet/src\""; - OTHER_CFLAGS = ( - "$(inherited)", - "-DDEBUG=1", - ); - OTHER_CFLAGS_QUOTED_FOR_TARGET_1 = "-DPACKAGE=\"\\\"supertuxkart\\\"\""; - OTHER_LDFLAGS = "$(inherited)"; - PREBINDING = NO; - PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; - PRODUCT_NAME = SuperTuxKart; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.6.sdk"; - }; - name = Debug; - }; - 9551C8250FC1B70000DB481B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9551C9F80FC1B87600DB481B /* Config.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = ( - i386, - ppc, - ); - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEPLOYMENT_LOCATION = NO; - GCC_DEBUGGING_SYMBOLS = used; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_ENABLE_SYMBOL_SEPARATION = NO; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 1; - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = ""; - GCC_VERSION = 4.0; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_MISSING_PARENTHESES = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; - GCC_WARN_SHADOW = NO; - GCC_WARN_UNUSED_FUNCTION = NO; - GCC_WARN_UNUSED_PARAMETER = NO; - HEADER_SEARCH_PATHS = "$(inherited)"; - HEADER_SEARCH_PATHS_QUOTED_1 = ""; - HEADER_SEARCH_PATHS_QUOTED_2 = ""; - INFOPLIST_FILE = "SuperTuxKart-Info.plist"; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", - "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", - ); - LIBRARY_SEARCH_PATHS_QUOTED_1 = "\"$(DEVELOPER_DIR)/games/supertuxkart/src/bullet/src\""; - LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = ""; - OTHER_CFLAGS = ( - "-DNDEBUG=1", - "$(inherited)", - ); - OTHER_LDFLAGS = "$(inherited)"; - PREBINDING = NO; - PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; - PRODUCT_NAME = SuperTuxKart; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; - STRIP_STYLE = "non-global"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "STK_XCode" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB923608733DC60010E9CD /* Debug */, - 1DEB923708733DC60010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 9551C8260FC1B70000DB481B /* Build configuration list for PBXNativeTarget "SuperTuxKart" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 9551C8240FC1B70000DB481B /* Debug */, - 9551C8250FC1B70000DB481B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/src/ide/Xcode/SuperTuxKart-Info.plist b/src/ide/Xcode/SuperTuxKart-Info.plist deleted file mode 100644 index d1caa503e..000000000 --- a/src/ide/Xcode/SuperTuxKart-Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - SuperTuxKart - CFBundleIconFile - stk.icns - CFBundleIdentifier - net.sourceforge.supertuxkart - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - SuperTuxKart - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.8 - CFBundleSignature - SPTK - CFBundleVersion - 0.8 - CSResourcesFileMapped - - - diff --git a/src/ide/Xcode/cleanupRelease.sh b/src/ide/Xcode/cleanupRelease.sh deleted file mode 100755 index afaceac7e..000000000 --- a/src/ide/Xcode/cleanupRelease.sh +++ /dev/null @@ -1,4 +0,0 @@ -find ./build/Release/ -name ".svn" -exec rm -rf '{}' > /dev/null \; 2> /dev/null -find ./build/Release/ -name ".DS_Store" -exec rm -rf '{}' > /dev/null \; 2> /dev/null -find ./build/Release/ -name "Makefile*" -exec rm -rf '{}' > /dev/null \; 2> /dev/null -exit 0 \ No newline at end of file diff --git a/src/ide/Xcode/stk.icns b/src/ide/Xcode/stk.icns deleted file mode 100644 index 806e9da080241cbd361a4b856f7bf75ecceee5a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34633 zcmeHw2Ut``_xIUC2SEe{#0u7^i9JyhHJZeds4<%O#uAMt#+o;Y=`r4=*?U7EY+twU zW|u0VNwqhO75l5yWod%6`Oe)1r7T8`Ax{%mYDitTx^ARfC7nix$E7d(X6O(AMu;Am9VU(p1l!*= zTlOrUcu}hl09$49%>A#R({~^3$XeiPsrtk1uYl}K;ZSA zDwR^JkBv_{dV&KE0oRV7I`ik{>p8{8Kk);m_{*Vt0c++qVPp`%C<>S(qBi2)>lCow zBbgpptvO>xc3vDKw#QpL2Pf}oi{5?HAKSwLw&Tq+8!5bR#{uhs+Osj=Z|NBXOwZ0v z9AI?C$}k47HvyAaIr{^w?OLO-FmhU?&?6(7>o0(|<0NxZ%pk5?ga0{?+#fdtIAk$tJ5Co_#<1Xzns2 zN$*u3(QEp_DeDO-AKQ>&tQkA)Z&asV5)^xW{P4i_Et1A z3t{qvX+9R^fZYJjRHIK4j3qaq8%Y8dUTui6{!<6*2m!1E)#y`$_ca(_HxxfC^M~M- z{?surKXkWHnZ{9V%zxQRdZe=F637IW$49>J901mwuRmVC8yx1%{c`6(*n0YAYEaA; zXdmZZkL^QlF1__wxkN$(P^&xlbfJ!G_%xUZyu%ldi^CfzZc3Pcg%iADMO z1($DsmUivj=@^ckd0e}FBHy{we?2!LCN_qDkTXNV`#E3C$(sT^>9T_)i11w0WyoF@?S zrE2-^cLTr^JDNSXe&g1i+&n{ZanZg1 zWnVeI8Ei?;&09BbW@mHradtFybT8~^_J966Yq|9{oH2ud{j(}YGIcwEUm#emJrbGt zv)eXctaA$hb||?^$+cWf8BW0$3}P-EV)}PxsC~e{i(-8am;l@7D8wPrZ~|O_4xw97WDWL?<52%D!!Q zQ0-4PLl9|@+nm0%+!n1IwF1Rsl3NcaaC6xPOs2m>09g5Sg07$S+y<<5U4h{TY{6Y_ zfr^$Lu2BOT%Z?rtIpd(?sj|>Ov+U?Wv2$Eg**QVao{eN@XJt`#%j;#waJ|JL%w>n( zt{0v4TPx>@%m3)H3B|SrmnW~yD675AQ#P za{lVM?3=f8bFnxfMKJ>#rvmg^`Nik|3m!YZKQ6Es&blz0rqfTW;{ZM^F{P6Tm*(jX zd$n~_JDC<>I!l^UvkW6>vWLy3>2FlmCShPz?djS_$(6fyA9z?t#?mdp(&|)~vyam0 zyDJ^)DXcQZm!6{6#>Qc8eR@da$VT@+*QbBqD6aAC?)voqG>&X^U$lI6z=1c>nr#%< z;UzWo{ zGXr&t)at_Xk=s|jWp7UJv2*h|LTc|9=iSUo(T4b~oIavUeUdo?j{Ub@AMuh;C+9XE zk;@i;furAdz0;F;hUI9o4?I@XzLM8M_^%f zo=f<@E1zXw(59;GFf_XHFAmra3tW7rNO9kznK$X+%Ll-Gs}@{QSh z_JP+%{i#de|M1KA!9T4J+_L>YF#O(~Ti0@8C6RjhF7Qf-jxLJiibCXJ0ztUqGk`OFEo;%rxor5xm=NXeqn(D^+`Q4l z-S^`*QyZHc4DcmI93#fAnhpI5YG%lP`+4AQ@UmGcl<8y9KA1qNt4Mxqd`SD-W#cqL z*{=SZfzipq585r!3VzeX$=gBuOQg1@jw1HMHl_I_#~<9jdJ+?@j()Bj=;FZ>^11TZ zcr+KMm6n%O*3^>UUrtIc$hoSRv2i6D8~yBXr~0x~K=D1aDWAfEw0k)@_wq~b6~7{> z%quXIUKJ~o)&pY-a_l-xAqeO41!`Sf`VthJLQdmo(^O9zx@_Ug|`!N zrL%@exqQBOEm)0q*b0nSx&?y!XL3GYAP@@pyx`Ay0yNC|Z-a6G+Qsbypi{qtA|a;n z!udiemp17I2bb)h@9DG!n1g?F^vAut;Gl@&Sqr&*5nB5kzO6teIw%hJU(|OaF#83! zLvz!0pGe5(@rA;0fmld|I|}(iez-t%P@&cSI1HO(<>Uw5ycYf@72|$@FA(u~LLo22 z#4VJ6P$5&QqxFes^y2=;x&5$J5}i_}kjNxF9$zG&#LgnAOsP_-H5#=Fw+wM{fnaa% z4d5ZsYLzM_N5xXg2_AHRBKggO6h?VlnSL>ss24O zF6C&}^_-$2L#eU2ENUZk^T;nq)aoOmV&meIQZrAU&%TvoC@Cp4l$Mm^juQ-wkz%arfInhlqW(BA51(Tv;=X=_k!I^ai}i3R#djN)jR)&T zj-dedh;cOb z=!<(FRDO&^{g%`Et3c&c0K?up46Hl6JZH1!N-9nbUAJxr2Zv6BW-R+voSt>Jq>4i} zBH~bX`85h$9J>u1{q~FxS8X|{Pr8_o`r=2J6@;KFF|!}k-pt9(DJUr|dr)0nRasM2 zZbVfwnpCN*j8sj8-L?@2D;_Kf#+RI2ydXvF4+tD6xgEs56Hqp$@Ga z1JH#EdBa#o2h^eaC~SB%+fT>1Mk++l_P*U=#lm_O+Wpzm zH6|6JXM2}UBy}dLP(mXWGR^kT^Y;nORj7Wpe`Ank73%EPT!k3U_Eb|H>eobv^2s2x z4prAr>=nK0b%@daN&u}uS!|5xiwa~vC3#<9!hJ$w`@Bl)l-(|1z2u*{bQi5H*snub zr(3GQ?EV(jfDa5C7u;x@bisT0+^w3cM{y?Xy4`InI68FdK5W)XN|JH@Zt(+J8@i!3 zykX<)?9pfFxGA$f{_i$vMDoQvqv3uHsj052pr;nLNgt@3_Ow1!K0tk_LVYMQ>w{B8 zSy|P6lRo4b&`QI(X$%n0FXcj*J6_^fa&OA{V^1z%w{)ut}45{DEUUY4(fYX3+b3R?Pa0Q1$ZRdx_ zn#q%@t4EhvTP*TTeU4_AYwBhgd_!T6|Ni{r55F3-=e;v-{7^4`LoroDy5(zdU0*uk zLo?Dq7osd@tdH$qC&#w{d;E?{@GajvRXj3ypjzpN7QNua$_HbZ-rgu~h81 z4Oktul+a3C4O(zL4~Vw>^4XNHTFKHhdTv9r;nk)ItvoG%m>=QqCkS&M=0^v}6NF|XAb3NHz5P-Em8pox;dL{NYmxCVk+v|$K%04oO)UHDhHm>{Ajo^5KTwPj< z9?t$`$^``39BFT-BYt{aLY_v1-Y!u9Rp`!;;qw~?V0I+e&{B?t)jhTW)P7Y2PIQrF zr?I<@BY`Zy2r0Q%NXiQf@+-+z5^Mmgf?8vKVZL83yD;BKwibZReqzke%g@c{eFG=g;8OF4-%|SZiqd-7k0R~c^ERYvwPn61FOX)XD;Lx zPzL`ZyRvL3h*W9HJCBWl-m@cI`i%Kt{F08^upO%cb{z=ZvS*<{E8Vm3r}e7>{XShb z>Fbak16Kw4qp#z^bt=>KM0#3MQgTZ2@uN^xl_pbZveraH?>D2=T8&bEw6a1v>EpHY zH_ltLWm>;*zHHg7?OQ|S!C|3cVwp%8;tw8EiVXSq?{02h9s(S7Yd}vfOH~@(2T0{; zf?BCo$;4O63q?w$3U~TSwM@z9>l7NPRLmFicmjz~_bp-*mDTwQY3P*Tp<%Ggd(L-Din$Nc&~|$I5O2ATt*>+T$YD$ zOkVEYLWIQoCqeg>K6K-fZZH_JdVREB!auOX(9hl3xO>N%)l>UA8{XakE?k7XYO6~M z@^cNP)wMO`LIGG`L!%vIId#^xH8nWBPt6BL3E5c9s;VN!Q)tN#9rVhVn*lP>$y7(G zsVXLJ@2G0)swzaE$j7Hp+kAY-;LdTj3SE+Y(JM$NqXJdSB;J+my?*GhmX&z2&-hjkB-BW$fm{$YgJuU?O(&JB|n@1YxfC# ze0;iZ!(B>e-Fk4mflaBdsl_@M7gpmow2nj^0sDn{0j{8XrH4P{se+DpySd>Gw1wD$j!NZ z+!1{D9d--Wu6h0NsUUDzZLCA^4ozd+WpapBO;t@DsmTDx`CSH2czeZFlKt@jFE$0% z*4%?X&OeqM2r!c(4z<;FMArjAjw(W7ClWF;!*qbfZotsj=6ttkY#&cVu2^9k&neI| z|LYChC>#C27WvfG;MbxIV60z&ZgO^?{sRUM?9;`=*$(ZOanZN^5C#~ZNv8ONHJ<3e zW-K`ZjCq3**0k~N-o@LagR{L2&h&lr7iC4;sibq=vz2cRU~(F;To*q92;4@Qqh=%N_6tI4DW<1%n+tELFk z8O9t2*6pso?l>8p!_P5vW1gOYwTdK!190X702lkm+B*n)Bv(BJeVsjs%PC-b4D!JA znRwu5E-B|t+zZSbL~KE(HptMkHF^$&#pr+1v*~6Geqh`F}x|*p5qd^oKmy#tN>kzwZznUR#nv~<{_v* zp1zKL&|ZkAau62)I4i&jO017wn*eN&Z`FJ?70K2|L`I!Q*-j|KI#yS4s&TrHwpkAy zrjc4KbsONa3TG7&U#ioMj{~P&F$xh+7#S643*R(R3SMW1cpAl8UQ5`=@f;fh0?SjO?C zNN7S1EeooKQ*B4jR+UxNH3Vw}cPQeD@Y3bzsOY$OQFypa7qxHp=N-Ag>e2TN`z_$O zHbN{GiG_SVFI*Cj)YA3@0$YP^tD$P4hN|{8LtNE3*4_u0O-M2T`*h;rwAjdKL6~4t z7oI}0b7DA7jbo>DL3%Mllp2E7Tpm}HfbRPf$>GY#eyCwN#Bw^#gi3BNY^M zBIRtDno?D4EH+$Aj|$;^gp2d&_kVcfH(>P{J<4-4w3`-z$TUwN5^(W2U0fVW?$9Ni zG6r74I#wXUi~2^@F{0{W5H1se;#-XWTAofp}@8SZx1HY z43rBbVm?rL>cIcuK zb9pj8FO;sLh$rT2;t$1U;)FD_loaRX`y<8tfLzC%g5|&FBT|Kv+LZ@XUI$bC@(K%z zj3p0_Nt8;lT$P6eXKshCQRq&PiTPndJSQX)2qZjtd|bk@epg8awl+7H%4cH3I5b@< z*T-1)pd>H9HWuJrQh*GKic89h3vORIogR-QHPJB<`c3Eq+PxjT^->YGog2>Mak1?J zzKAPOM`Tr1V6i-GnGIc~Jp4nk8c5EW+QPzO1Pt6sO>x1EtfO%%{(-&0p(3SPql(G& z2nFD6hxep7ig*%UI39!&h;Zy+M+w7k7SpClK>_|NDk?M-7z{;*Vq-~hRT4m+G52OZ zPRes)xFH7)1n(8_>00qLx`>FQGr|xG-wq?DrwRmITF63yK!&D|Oes-_j+d8~pm!lZ zH|N&1D_5`IxOw&BxnoC@6XU;H9VptrcYnAJ&xb{!zXdD6=Sz?%A7`e+lHX8~cEHq3 z6`v8VGL$I2BU`-AuG+8exgZ*cIw{h=YD;h_S|CK901L`=u| zR)g~$KU^q~OYp=Ommhb|^LKCyjpK{8iD?<|Kf(|4;N}TRTkyMH3cLF+j=(AyB^e*xFT%8K%{yuWB9-OI&T|c194q%1q`8-O{ z54}<7r$k+)hbRtup%^2gP{S#m2utE5fYQOMatbat_9K=KL{vUGeb*jfO0{A?0UN7| zP9mX|Sb)AKtPe}bmj$B<9*Ihd1$jI_0YedhWncO6zz)RnIZ=DjaEX9 zNb%dznxRl>)v|Ef*CzB60G|>t^*Tq~Oq|KQ%T5KGNm^{8kT0f$y(L1NF@@MPJaESk zRlQO5P0hqhNCO%9ckxB&;O2`n2C?Qk84X1bvY(1|% z7C@&Odj7CC#5mv6qP5ZOdDnf_mbK$m=-hbU5uLv(sZb)6AqAm8Bo_09Vi7l-i*p1= zXeANzLc@g|z8e=4xFQj{-b6}$luoV?{W5Q;3|J#p@Tcv9aXWPqw6A3%z9JV2b#Q`S5$%{+V67dC6VHh8~izlQ6?WCwusBRLKIwmoh{}B&ZKb^Z6 zwF_)VCMy&YTAwMt8>%K<37LQ^z%^7Vlwxy*6t|}kH9Slz5rkvwQCo2JL9IdWqCl(= zN@WpoY5X3zY|OimrrQORY>w&h^dl;f5Ua$;B}Bp($-?;bJRlaM3~~I03PgB7n};Kt zD@9pgT|-edghIKDFO!R;Qn@-Rm5)dCSY0I>Htd3SHi!73VYsLxt#E;WM+v)0u)7Wj zg`r|8UxBNhBs45UAP5nL%H+H-sYuAB1a83+DGpJILLyQ~6%wgJsXdgk9KC=aj~cTB zy7$?rR$xnnA{j2}5}r6*g!f{B64!!Io=hZ=QKEj_1Htr&-Y-=N!z2QE2zD^?02txQ0aj=x9*dq_!x9@;Bm?syhv8FiL<0#Xr zBh>2X*?5p?z&3biVdNp5SSnX3DA~rmdTY$g+hkX zmP*uesazC}lZTLpE0{>KUnUYtcK!a=1rd$pC?bPpsvKM49|oXm((ibV>!0u#~QtyRnAiU<`t*;Q(>3YSHZ zKqZjLwHhs@vqPdPbge7ZYD&YG;*$3J@UuIYHoY zI6_0qS*4aMkOnR?8ZA~`r*c9da%xm0GL}uIE}^M5J7! z*Q*1SY`QY)AT844sPW{0+6^C(l3b1}v0NLIbTsSA-NF)1nNx95Q9)r*QAtUu)zCQ# zKDgYdyM0Km!FEUJbrD+naDj@UR&$grx`!w&L$CMKdg=9AEuJ?(Y7(545I~_1-(J3r zyX$hQ)D`Q8WDSLd27|HW;1{Lc6Tg*1yO_GNvXUFI3bjs$bm+R#|CL$pgCWoI*}MBQMV{-wG`{JWE3v+GADd zN_6k(J?76pwHs~@n0o~J-5Zaf#sj39i!cycZTN+Kiq*uf?cBYqN7txRrxbDe{`=OQ z2kW)t%PI{$=$B!mn6zQTy1rKOJHTGJ&^`K_)&a&SXg$qx%5#Mwt0~+sG=U)# zV3zx?Mlj?I<%z3V>ApZdMHo^*GLtW4CMPCkX2lQ+VaS9NiHV1ksYGi;{t{9zd|d$S zAFrojOphC5!h4H73w5T3jR1E)hZ=5BNo=B0_&3{ z9aiPQTqX`LzQ3(8PV~aM7!j~*i4cO?^_+kqySRMrGMT;n0ZOjRGf8e)F}j3`)2@iHG{Fq zbm)Z50&kF;@w-}5cl|_a(y3eYcKCb&Sefvp87 z*LN%ecfPA00(Sw8LA#+ozP@)6lXIPmn4AU7ZV0BohUxs_tzmZZxBL3|`s_r^E`7TN z%&x|S*{v+xunI9d%s3Nn>`-BUde4FlXu;1K8l*znAY2U;NQmunDw_ zi#mGKhz^&=pxxDZ3zu?2za7)p8IO$ zdwO4JJnq0(Gy(0X8pI`O%x+=VArmLB$|g7F5A?Q1%#PXZSmF+m=CPO^LN9g$M~<5J z{o-+by6X#?GTuH?bx)|LAzb=erN*ioShtPtZC4WeWn-gku8CCv&M|Y z{b5Ecpq-(sPX`3-B$h$DH5Ng;?j!r)*c{muv^xbh)&o5~9nFKb1!|XV2I^`o0d;UI z+ae}sWya*P!7}z{U5~vX>E-~NH3Dqa2(V$%Rv|PiGlX`{B0y$k2FUa{)bZR?bMOlh zc?7=@B|`8^M`K->rvcZZM{8qH3%q|fLs$o!pj9r-(W=8Z6xf~$GdzWPX(S3~5NHZR zIg%(_6d39^@(u_r0m4Z_^Dxi>CftH58e)X^A|g9&5Wb5(co(uq_miJf}p z%AqgBfIlfgWJw0@lTW3^MnxmqmXw7*yg>YjhLCV-XI@E9;v{mCtPW?KJuet90C-6O zE*n$Nq$b2C;QX8r7Za6y;{ncXW!(WnPE(kdp0|?{Q%_tC;v*^~1FI$Hvoey943*Ro z=kxgJnBzsYx2_~)C;+T4AEq;Nl5FUiJ}LEz7Z2bA6yTVQiy4VYeu;ibRHAD_{Gqsc zpT{SpT($?$AIDVzV{MZUr)8cvd&#f_i{MdZ*XYZchmjV&Xwd5heaA`kO3yr&nv`@J zt-O(!Q<4#sID6qzR=RfQib;L3db>qn-T&gLWXw(_0_F$2GR)-9#4FCGrJuZu+xjcl zveM*RR!nr4A}}KY2i5tM!zn4LDd}k$nba}Y%*^A*Po6oOb>SNBnNOZdl!cC`0GJwp z|BAq4!M#i8&z?S!ni#DV1cwTw@{n!ozFac%tx zTX=kmOg}z@-i>DfYeLYLdFGup#?!lFywE#aywF=xywDp-ywID!D${-q58|*9!E&y` z4H12hQyNo;TNu2zi^J_kBz8jz?hjOW#rPoFq{mUz^}~JXB|x8Io^t`%U1sb^E<^>a zH;W3SjH1AJuyG%`c#HgO5hgj+louBoXhXKlY{;_i zk#l`?VB3kxT8J_1o*s*{Fa7Mi`(6Te!6gP7 zk)<`2m>uh5;1r-4$;r1aXl6$mx^(v(_2-`v+i#9Na0DD1+mU%xzGuB1i3p&<*tv^W z2ilH|iqj9(B%18V(#k@EE2j(7%MC3_Yub|hgkAPOU@crYKF6{}NXJG$4?FViv%0fw z3I425+t`kTQ#h&Ogj4rb5ugjw4%bz(25?fwMQ`vfp6p1)02mjU2STQy$uR>rJK5GYf+$AGnA{+?(Sq8+i?m z*?>N1-JoL@y{^q1v;8O1j@b=p-l4|L3qtar@9 zL&xlD%xwShp-8ihSB&nn2FGkxOULXw`31D`bQcXR9kVN%Ic7s)E&Is22$aI|BJ2|M zu$UdQ4{f~p-i`dSco6xMf_s|n0-O(LwRFp3X^U>z8&Tn$)=WHZOSkOg7~T99CLX$F z=eBUmPKtf2bqnvCCT>|&^4K1&7M} zw*5Ui;(E-+UD;C_c*g)zu!0sGuU6=2N5|`58+k0+$O~w2p|-H_=)(=@2CR3IUZS0( zO)b2DYUV@#rzP`lz1eX4yQZCgwG$q+f8>Qs*EN`QjJ{Xa6MOnV$3w3o&T_ainjDBy zU<}Wp2wS~=`A;|eh{;cAZL;B3y`lrQwb^WAczupzdg@sYp=xm(L(mn~A3bp|9l~P+ zWw=;>9fbziAHeq3VOk8p`w94ifD1JMjw|5s)R0!?U!AYxU>?&`WQ`^pAqX)Kx{>`Tzn z4DrjS@~jceLOiU9Q|xFg>e9LW@IU{I+kSIenF-4(X@O;7R^(7e%mpKrLhpKi#?!5(R5azcUzT3opV83A~WN z3kkfCzzYeykiZKGJc$HY(i)g+Kor_RDv;XKe^7x98uy zzo7VSBld(S+gbuc@Q`=ofB;UL1_Ugdgpz@6%6?M4>OHChBF1ePWRDov-&OQ!L-tVG zfTSff+f$+|D|ST zRg3E24+yKOEUbQ}jN)f!+)#2vRL!GMVbZfhcq;Mp_s!uwS30AhMYYTk1<%_IUO>|w z-%z)Q*MwI16LhpZ*Wh>^Ii)oxIiS_ycYk8@ynpj~5Awg?%(7d0qqXK^-sfC_ZnQq) zfx}0^UEDf%y0Xu?0(zQeqlvxq^~bWu%#$9~^*P*)quHTjr#>*^|AIZfrQ@51%+KTM zn2HSRW;Kv{lJ{lQ?fyF}Km#YdzCL+!JA6+Rh9mW{uJv!H`3oVWxHqEmthWP_ahfaI zGoR*FH``35wO6J#ug{rw&&u^-xlT>q1#{fSl%QzNsI})`AJTc&n{`b{*Da&z*rqj! ze9XhuXJz}aT(g(e+;xS|3k;9p*fi<>LpslTL(hDT!*{linf4lIG~GY=+Zq_vjOP7E zZysuvV(Ia3xBsbL6w|T>h0iMXhH`^j(tMg2^o#$!(8B`4@Dvr;I&$R5(f(WLpqlzj zHBmm~_UE;Sx6gEQKY;xVH5g=iGat+UgQq9>lqK-|&SSzq&p-7s-0=|lQ?};mv$xpAHL1@F{!cH_ zrz`Pz`M>pa#GWDdFv-bj=%=54x7pVWL&&p+&+Z>&WQ6`jo$c;8}b8$=%bm-*8#Eydv3DyI5R%issI0NP>#u-Ftl zl1+!r78BbbveL=KpSh);A8T67|GXoonfU{_ zb+Wf_@*gkudETrn()+4`e~W9|@E5k=+Yr$<{6!7yT3p+Pf9rNh+wgy=fB#bZ|84#< z6U5V^@fguI@+bH+T+5-2_>=jT*@H8_)S@i{z9Q6eINcW9@yCNK%j}_z_>(EE@ITN- z?RRK}|Icm1AKztaIkb&cTDQtFcl;UTKi~f1bAPnH0JOt*!2V;Q>ih}n$Nt`}wcQji z`bW4IT!`@den7Kh1s3b^-t#q!D=%Hz6)>RXdQ{d~vhHZQ@-#gBxH?LpM*tBWm zJHv)O*OU3NVd^o%X_b4 s7~I+d(wxNKd}fRqHR`p$(U*Ud{fpu+B=AB4FC_3n0xu-+d?oPz02qS9IRF3v diff --git a/src/ide/codeblocks/README b/src/ide/codeblocks/README deleted file mode 100644 index a3e69131e..000000000 --- a/src/ide/codeblocks/README +++ /dev/null @@ -1,35 +0,0 @@ -Basically 4 setups are included in the vc8-project: ---------------------------------------------------- -The first one is the one currently used and supported, -the others are more 'historical' and will be remove soon. - -BulletDebug/BulletRelease: - main setup (Debug/Relase) + BULLET - The debug version links in the optimised bullet (and sdl and plib) versions, since i is assumed that - mostly STK is being debugged, not everything. Using debug for everything creates a really slow executable. - -ReplayDebug/ReplayRelease: - main setup (Debug/Relase) + HAVE_GHOST_REPLAY - -Debug/Release: - main setup with openAL & vorbis: HAVE_OPENAL + HAVE_OGGVORBIS - -PlibSoundDebug/PlibSoundRelease: - no ogg-sound, no openAL, no vorbis, it is gonna be obsolete, when we move to OpenAL/ogg-vorbis completely - - -Create these environment-vars pointing to the folders of your installations: ----------------------------------------------------------------------------- -STK_LIB: pointing to a directory containing all libs -STK_INCLUDE: pointing to a directory containing all header files. -A separate package will be made available which contains all necessary windows -files to compile STK. If you prefer to have the packages in separate -directories, please add your directories to the paths in visual studio. - -Add the folders of the runtime-dlls to the PATH-variable, this is what i had to put: -(or copy the .dll files in the root directory of supertuxkart). - -Remarks for OpenAL-installation: --------------------------------- -My version of OpenAL 1.1-SDK didn't put its includes in OpenAL/include/AL but in OpenAL/include. -if yours did the same, create the folder OpenAL/include/AL and copy all the includes there. diff --git a/src/ide/codeblocks/Readme - How to run stk on codeblocks.txt b/src/ide/codeblocks/Readme - How to run stk on codeblocks.txt deleted file mode 100755 index 4f232a52f..000000000 --- a/src/ide/codeblocks/Readme - How to run stk on codeblocks.txt +++ /dev/null @@ -1,43 +0,0 @@ -==================================================================================== - -Steps: -* Go to src->ide->codeblocks and open "supertuxkart workspace" file. -* Codeblocks should open automatically with 4 projects under the workspace. -* Build all the 4 projects in the following order: - 1. enet - 2. bullet_lib - 3. Irricht - 4. Supertuxkart -( You have to right click on each of the project in the project workspace -and select "build") -* Select target as "debug" for debug version and "release" for release version -* To do this: - 1. Right-click and activate the project - 2. Go to "Build -> Select target" in the main menu and choose debug or release - -* Make sure you have all the dependancy files extracted in root folder of stk -( where you will find folders named src and data) -* Your "dependancies" folder and the .dll files should be here - -* Right-click and activate project "supertuxkart" and select - a. Build and Run for release version - b. Start Debugging for debug version - - -[ If you get error "Irricht.dll" not found or problem in dynamic linking - 1. Go to "..\..\lib\Win32-gcc" and copy the "Irricht.dll" file to root folder - * You will find this file after you have built you "Irricht" project ] -==================================================================================== - -Dependancies: -* Download "dependencies_for_0.8" and "dependencies_for_0.8_mingw" -* Extract both in the root stk folder -* In dependancies folder, cut everything from inside the include and lib folder -and paste it inside the dependancies folder itself - -* Also copy libwin2_32.a and libwinmm.a to dependancies folder - -==================================================================================== -Tested on codeblocks 10.05 with mingw 4.4.1 compiler -==================================================================================== -==================================================================================== \ No newline at end of file diff --git a/src/ide/codeblocks/bullet_lib.cbp b/src/ide/codeblocks/bullet_lib.cbp deleted file mode 100644 index 848fc814b..000000000 --- a/src/ide/codeblocks/bullet_lib.cbp +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - diff --git a/src/ide/codeblocks/bullet_lib.depend b/src/ide/codeblocks/bullet_lib.depend deleted file mode 100644 index 6d956ed37..000000000 --- a/src/ide/codeblocks/bullet_lib.depend +++ /dev/null @@ -1 +0,0 @@ -# depslib dependency file v1.0 diff --git a/src/ide/codeblocks/bullet_lib.layout b/src/ide/codeblocks/bullet_lib.layout deleted file mode 100644 index 47bc15648..000000000 --- a/src/ide/codeblocks/bullet_lib.layout +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/ide/codeblocks/enet.cbp b/src/ide/codeblocks/enet.cbp deleted file mode 100644 index 52fc898cc..000000000 --- a/src/ide/codeblocks/enet.cbp +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - diff --git a/src/ide/codeblocks/enet.depend b/src/ide/codeblocks/enet.depend deleted file mode 100644 index 3d2e1eb1f..000000000 --- a/src/ide/codeblocks/enet.depend +++ /dev/null @@ -1,85 +0,0 @@ -# depslib dependency file v1.0 -1300395742 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\callbacks.c - "enet/enet.h" - -1310445298 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\enet.h - - "enet/win32.h" - "enet/unix.h" - "enet/types.h" - "enet/protocol.h" - "enet/list.h" - "enet/callbacks.h" - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\win32.h - - - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\unix.h - - - - - - -1235868408 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\types.h - -1310445298 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\protocol.h - "enet/types.h" - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\list.h - - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\callbacks.h - - -1300395742 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\compress.c - - "enet/enet.h" - -1310444832 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\host.c - - - "enet/enet.h" - -1300395742 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\list.c - "enet/enet.h" - -1300395742 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\packet.c - - "enet/enet.h" - -1310444832 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\peer.c - - "enet/enet.h" - -1310444832 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\protocol.c - - - "enet/utility.h" - "enet/time.h" - "enet/enet.h" - -1235868408 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\utility.h - -1235868408 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\\enet\time.h - -1323123324 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\unix.c - - - - - - - - - - - "enet/enet.h" - - - -1331421418 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\win32.c - - "enet/enet.h" - diff --git a/src/ide/codeblocks/enet.layout b/src/ide/codeblocks/enet.layout deleted file mode 100644 index e5c191a88..000000000 --- a/src/ide/codeblocks/enet.layout +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/ide/codeblocks/supertuxkart.cbp b/src/ide/codeblocks/supertuxkart.cbp deleted file mode 100644 index b996457e2..000000000 --- a/src/ide/codeblocks/supertuxkart.cbp +++ /dev/null @@ -1,684 +0,0 @@ - - - - - - diff --git a/src/ide/codeblocks/supertuxkart.depend b/src/ide/codeblocks/supertuxkart.depend deleted file mode 100644 index b29bc2cc0..000000000 --- a/src/ide/codeblocks/supertuxkart.depend +++ /dev/null @@ -1,6472 +0,0 @@ -# depslib dependency file v1.0 -1342824518 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\addon.cpp - "addons/addon.hpp" - - - "io/file_manager.hpp" - "io/xml_node.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - -1343087916 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\addon.hpp - - - "io/file_manager.hpp" - "utils/time.hpp" - -1352873192 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\io\file_manager.hpp - - - - - - "io/xml_node.hpp" - "utils/no_copy.hpp" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrstring.h - "irrTypes.h" - "irrAllocator.h" - "irrMath.h" - - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrtypes.h - "IrrCompileConfig.h" - - - "assert.h" - - - -1352165168 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrcompileconfig.h - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrallocator.h - "irrTypes.h" - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrmath.h - "IrrCompileConfig.h" - "irrTypes.h" - - - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ifilesystem.h - "IReferenceCounted.h" - "IXMLReader.h" - "IFileArchive.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ireferencecounted.h - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ixmlreader.h - "IReferenceCounted.h" - "irrXML.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrxml.h - - "IrrCompileConfig.h" - - - - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ifilearchive.h - "IReadFile.h" - "IFileList.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ireadfile.h - "IReferenceCounted.h" - "coreutil.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\coreutil.h - "irrString.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\path.h - "irrString.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ifilelist.h - "IReferenceCounted.h" - "path.h" - -1350366706 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\io\xml_node.hpp - - - - - - - - - - - "utils/leak_check.hpp" - "utils/no_copy.hpp" - "utils/time.hpp" - -1352247034 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\scolor.h - "irrTypes.h" - "irrMath.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\vector2d.h - "irrMath.h" - "dimension2d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\dimension2d.h - "irrTypes.h" - "irrMath.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\vector3d.h - "irrMath.h" - -1344304468 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\leak_check.hpp - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\no_copy.hpp - -1349162248 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\time.hpp - - - - - - - "graphics/irr_driver.hpp" - -1349128420 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\irr_driver.hpp - - - - - - - - "post_processing.hpp" - "utils/aligned_array.hpp" - "utils/no_copy.hpp" - "utils/ptr_vector.hpp" - "utils/vec3.hpp" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ivideodriver.h - "rect.h" - "SColor.h" - "ITexture.h" - "irrArray.h" - "matrix4.h" - "plane3d.h" - "dimension2d.h" - "position2d.h" - "SMaterial.h" - "IMeshBuffer.h" - "triangle3d.h" - "EDriverTypes.h" - "EDriverFeatures.h" - "SExposedVideoData.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\rect.h - "irrTypes.h" - "dimension2d.h" - "position2d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\position2d.h - "vector2d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\itexture.h - "IReferenceCounted.h" - "IImage.h" - "dimension2d.h" - "EDriverTypes.h" - "path.h" - "matrix4.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iimage.h - "IReferenceCounted.h" - "position2d.h" - "rect.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\edrivertypes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\matrix4.h - "irrMath.h" - "vector3d.h" - "vector2d.h" - "plane3d.h" - "aabbox3d.h" - "rect.h" - "irrString.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\plane3d.h - "irrMath.h" - "vector3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\aabbox3d.h - "irrMath.h" - "plane3d.h" - "line3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\line3d.h - "irrTypes.h" - "vector3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrarray.h - "irrTypes.h" - "heapsort.h" - "irrAllocator.h" - "irrMath.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\heapsort.h - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\smaterial.h - "SColor.h" - "matrix4.h" - "irrArray.h" - "irrMath.h" - "EMaterialTypes.h" - "EMaterialFlags.h" - "SMaterialLayer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ematerialtypes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ematerialflags.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\smateriallayer.h - "matrix4.h" - "irrAllocator.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imeshbuffer.h - "IReferenceCounted.h" - "SMaterial.h" - "aabbox3d.h" - "S3DVertex.h" - "SVertexIndex.h" - "EHardwareBufferFlags.h" - "EPrimitiveTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\s3dvertex.h - "vector3d.h" - "vector2d.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\svertexindex.h - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ehardwarebufferflags.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\eprimitivetypes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\triangle3d.h - "vector3d.h" - "line3d.h" - "plane3d.h" - "aabbox3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\edriverfeatures.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\sexposedvideodata.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrlichtdevice.h - "IReferenceCounted.h" - "dimension2d.h" - "IVideoDriver.h" - "EDriverTypes.h" - "EDeviceTypes.h" - "IEventReceiver.h" - "ICursorControl.h" - "IVideoModeList.h" - "ITimer.h" - "IOSOperator.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\edevicetypes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ieventreceiver.h - "ILogger.h" - "Keycodes.h" - "irrString.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ilogger.h - "IReferenceCounted.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\keycodes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\icursorcontrol.h - "IReferenceCounted.h" - "position2d.h" - "rect.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ivideomodelist.h - "IReferenceCounted.h" - "dimension2d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\itimer.h - "IReferenceCounted.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iosoperator.h - "IReferenceCounted.h" - "irrString.h" - -1332693682 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\post_processing.hpp - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ishaderconstantsetcallback.h - "IReferenceCounted.h" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\aligned_array.hpp - "LinearMath/btAlignedObjectArray.h" - - -1326053842 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btalignedobjectarray.h - "btScalar.h" - "btAlignedAllocator.h" - - - - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btscalar.h - - - - - - - - - - - - - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btalignedallocator.h - "btScalar.h" - -1317337380 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\ptr_vector.hpp - - - "utils/aligned_array.hpp" - -1335740862 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\vec3.hpp - - - "LinearMath/btVector3.h" - "LinearMath/btMatrix3x3.h" - "utils/constants.hpp" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btvector3.h - "btScalar.h" - "btMinMax.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btminmax.h - "LinearMath/btScalar.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btmatrix3x3.h - "btVector3.h" - "btQuaternion.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btquaternion.h - "btVector3.h" - "btQuadWord.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btquadword.h - "btScalar.h" - "btMinMax.h" - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\constants.hpp - -1343779420 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\string_utils.hpp - - - - - -1350157788 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\addons_manager.cpp - "addons/addons_manager.hpp" - - - - - - - "addons/inetwork_http.hpp" - "addons/request.hpp" - "addons/zip.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "states_screens/kart_selection.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/string_utils.hpp" - -1320385088 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\addons_manager.hpp - - - - "addons/addon.hpp" - "io/xml_node.hpp" - "utils/synchronised.hpp" - -1344303342 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\synchronised.hpp - - -1278008626 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\pthread.h - - "config.h" - - - - - "need_errno.h" - - - - -1278008576 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\sched.h - "need_errno.h" - - - - -1348120280 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\inetwork_http.hpp - "addons/request.hpp" - -1319565188 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\request.hpp - - "utils/leak_check.hpp" - "utils/synchronised.hpp" - -1319565188 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\zip.hpp - -1352942436 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_properties.hpp - - - - - "audio/sfx_manager.hpp" - "karts/kart_model.hpp" - "io/xml_node.hpp" - "race/race_manager.hpp" - "utils/interpolation_array.hpp" - "utils/vec3.hpp" - -1342987538 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\sfx_manager.hpp - - - - - - "utils/no_copy.hpp" - "utils/vec3.hpp" - -1267382722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\al\al.h - -1353882294 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_model.hpp - - - "utils/no_copy.hpp" - "utils/vec3.hpp" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ianimatedmeshscenenode.h - "ISceneNode.h" - "IBoneSceneNode.h" - "IAnimatedMeshMD2.h" - "IAnimatedMeshMD3.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenenode.h - "IAttributeExchangingObject.h" - "ESceneNodeTypes.h" - "ECullingTypes.h" - "EDebugSceneTypes.h" - "ISceneNodeAnimator.h" - "ITriangleSelector.h" - "SMaterial.h" - "irrString.h" - "aabbox3d.h" - "matrix4.h" - "irrList.h" - "IAttributes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iattributeexchangingobject.h - "IReferenceCounted.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\escenenodetypes.h - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ecullingtypes.h - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\edebugscenetypes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenenodeanimator.h - "IReferenceCounted.h" - "vector3d.h" - "ESceneNodeAnimatorTypes.h" - "IAttributeExchangingObject.h" - "IEventReceiver.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\escenenodeanimatortypes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\itriangleselector.h - "IReferenceCounted.h" - "triangle3d.h" - "aabbox3d.h" - "matrix4.h" - "line3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrlist.h - "irrTypes.h" - "irrAllocator.h" - "irrMath.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iattributes.h - "IReferenceCounted.h" - "SColor.h" - "vector3d.h" - "vector2d.h" - "line2d.h" - "line3d.h" - "triangle3d.h" - "position2d.h" - "rect.h" - "dimension2d.h" - "matrix4.h" - "quaternion.h" - "plane3d.h" - "triangle3d.h" - "line2d.h" - "line3d.h" - "irrString.h" - "irrArray.h" - "IXMLReader.h" - "EAttributes.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\line2d.h - "irrTypes.h" - "vector2d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\quaternion.h - "irrTypes.h" - "irrMath.h" - "matrix4.h" - "vector3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\eattributes.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ibonescenenode.h - "ISceneNode.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ianimatedmeshmd2.h - "IAnimatedMesh.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ianimatedmesh.h - "aabbox3d.h" - "IMesh.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imesh.h - "IReferenceCounted.h" - "SMaterial.h" - "EHardwareBufferFlags.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ianimatedmeshmd3.h - "IAnimatedMesh.h" - "IQ3Shader.h" - "quaternion.h" - "irrpack.h" - "irrunpack.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iq3shader.h - "irrArray.h" - "fast_atof.h" - "IFileSystem.h" - "IVideoDriver.h" - "coreutil.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\fast_atof.h - "irrMath.h" - "irrString.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrpack.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrunpack.h - -1353028222 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\race_manager.hpp - - - - "network/remote_kart_info.hpp" - "race/grand_prix_data.hpp" - "utils/translation.hpp" - "utils/vec3.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\remote_kart_info.hpp - - - -1354018156 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\grand_prix_data.hpp - - - - - - "utils/translation.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\translation.hpp - - - - "utils/string_utils.hpp" - "tinygettext/tinygettext.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\tinygettext.hpp - "dictionary.hpp" - "dictionary_manager.hpp" - "language.hpp" - -1300401266 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\dictionary.hpp - - - - "plural_forms.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\plural_forms.hpp - - -1306089712 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\dictionary_manager.hpp - - - - - - "dictionary.hpp" - "language.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\language.hpp - - -1352942436 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\interpolation_array.hpp - - - -1342478208 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_properties_manager.hpp - "utils/ptr_vector.hpp" - - "network/remote_kart_info.hpp" - "utils/no_copy.hpp" - -1354838506 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\kart_selection.hpp - - "guiengine/screen.hpp" - "states_screens/state_manager.hpp" - -1351464880 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\screen.hpp - - - - "utils/cpp2011.h" - - - "config/stk_config.hpp" - "guiengine/abstract_top_level_container.hpp" - "guiengine/engine.hpp" - "guiengine/event_handler.hpp" - "guiengine/widget.hpp" - "input/input.hpp" - "utils/ptr_vector.hpp" - "utils/leak_check.hpp" - -1336956134 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\cpp2011.h - -1353277350 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\stk_config.hpp - "utils/no_copy.hpp" - - - -1318723258 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\abstract_top_level_container.hpp - - - "utils/ptr_vector.hpp" - "guiengine/widget.hpp" - -1341546818 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widget.hpp - - - "guiengine/event_handler.hpp" - "guiengine/skin.hpp" - "utils/constants.hpp" - "utils/ptr_vector.hpp" - -1316736444 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\event_handler.hpp - - - "input/input.hpp" - "utils/leak_check.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\input.hpp - - - -1317185692 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\skin.hpp - - - - - - - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguiskin.h - "IAttributeExchangingObject.h" - "EGUIAlignment.h" - "SColor.h" - "rect.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\eguialignment.h - -1327883274 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\engine.hpp - - "utils/constants.hpp" - "utils/ptr_vector.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\state_manager.hpp - - "config/player.hpp" - "guiengine/abstract_state_manager.hpp" - "utils/ptr_vector.hpp" - -1340296410 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\player.hpp - - "config/user_config.hpp" - "utils/no_copy.hpp" - - -1351576130 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\user_config.hpp - - - - - - "graphics/camera.hpp" - "utils/constants.hpp" - "utils/no_copy.hpp" - "utils/ptr_vector.hpp" - "utils/time.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\camera.hpp - - - - - "io/xml_node.hpp" - "utils/no_copy.hpp" - "utils/aligned_array.hpp" - "utils/vec3.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\abstract_state_manager.hpp - - - "guiengine/engine.hpp" - "guiengine/screen.hpp" - "utils/leak_check.hpp" - -1353374884 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track.hpp - - - "LinearMath/btTransform.h" - "graphics/material.hpp" - "items/item.hpp" - "tracks/quad_graph.hpp" - "utils/aligned_array.hpp" - "utils/translation.hpp" - "utils/vec3.hpp" - "utils/ptr_vector.hpp" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\bttransform.h - "btMatrix3x3.h" - -1343605576 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\material.hpp - "utils/no_copy.hpp" - - - - -1346810450 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\item.hpp - "utils/leak_check.hpp" - "utils/no_copy.hpp" - "utils/vec3.hpp" - - -1344409076 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\quad_graph.hpp - - - - "tracks/graph_node.hpp" - "tracks/quad_set.hpp" - "utils/aligned_array.hpp" - "utils/no_copy.hpp" - - -1346712570 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\graph_node.hpp - - - - - "tracks/quad.hpp" - "tracks/quad_set.hpp" - "utils/vec3.hpp" - -1350852514 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\quad.hpp - - "utils/vec3.hpp" - -1329392722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\quad_set.hpp - - - "tracks/quad.hpp" - "utils/vec3.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_manager.hpp - - - - -1348188244 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\inetwork_http.cpp - "addons/dummy_network_http.hpp" - "addons/inetwork_http.hpp" - "addons/network_http.hpp" - - -1348120280 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\dummy_network_http.hpp - "addons/request.hpp" - "addons/inetwork_http.hpp" - -1348120280 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\network_http.hpp - - - - - - - "addons/inetwork_http.hpp" - "addons/request.hpp" - "utils/synchronised.hpp" - -1278009976 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\curl\curl.h - "curlver.h" - "curlbuild.h" - "curlrules.h" - - - - - - - - - - - - - "easy.h" - "multi.h" - "typecheck-gcc.h" - -1278009976 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\curl\curlver.h - -1278009976 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\curl\curlbuild.h - - - -1278009976 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\curl\curlrules.h - -1278009976 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\curl\easy.h - -1278009976 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\curl\multi.h - "curl.h" - -1278009976 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\curl\typecheck-gcc.h - -1351983538 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\network_http.cpp - "addons/network_http.hpp" - - - - - - - - "addons/news_manager.hpp" - "addons/request.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "states_screens/addons_screen.hpp" - "states_screens/main_menu_screen.hpp" - "utils/string_utils.hpp" - "utils/time.hpp" - "utils/translation.hpp" - - -1342755616 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\news_manager.hpp - - - - "utils/synchronised.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\addons_screen.hpp - "addons/addons_manager.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/label_widget.hpp" - "states_screens/dialogs/addons_loading.hpp" - -1319564774 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\label_widget.hpp - - - "guiengine/widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1324583334 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\addons_loading.hpp - "addons/addon.hpp" - "addons/addons_manager.hpp" - "guiengine/widgets.hpp" - "guiengine/modaldialog.hpp" - "utils/synchronised.hpp" - -1283631882 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets.hpp - "guiengine/widgets/bubble_widget.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "guiengine/widgets/list_widget.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "guiengine/widgets/progress_bar_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "guiengine/widgets/model_view_widget.hpp" - "guiengine/widgets/text_box_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "guiengine/widgets/check_box_widget.hpp" - -1319564774 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\bubble_widget.hpp - - - "guiengine/widget.hpp" - "utils/leak_check.hpp" - -1319564774 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\button_widget.hpp - - "guiengine/widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1323303924 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\icon_button_widget.hpp - - "guiengine/widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1323303924 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\list_widget.hpp - - "guiengine/widget.hpp" - "guiengine/widgets/button_widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1319564774 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\dynamic_ribbon_widget.hpp - - "guiengine/widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1324583334 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\ribbon_widget.hpp - - "guiengine/widget.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguistatictext.h - "IGUIElement.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguielement.h - "IAttributeExchangingObject.h" - "irrList.h" - "rect.h" - "irrString.h" - "IEventReceiver.h" - "EGUIElementTypes.h" - "EGUIAlignment.h" - "IAttributes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\eguielementtypes.h - "irrTypes.h" - -1341546818 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\spinner_widget.hpp - - "guiengine/widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1319564774 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\progress_bar_widget.hpp - - "guiengine/widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1353289332 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\model_view_widget.hpp - - "graphics/irr_driver.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "utils/aligned_array.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1323303924 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\text_box_widget.hpp - - "guiengine/widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1323303924 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\check_box_widget.hpp - "guiengine/widget.hpp" - "utils/leak_check.hpp" - "utils/ptr_vector.hpp" - -1316714884 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\modaldialog.hpp - - "utils/ptr_vector.hpp" - "guiengine/abstract_top_level_container.hpp" - "guiengine/skin.hpp" - "input/input_manager.hpp" - "utils/leak_check.hpp" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguiwindow.h - "IGUIElement.h" - "EMessageBoxFlags.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\emessageboxflags.h - -1325446652 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\input_manager.hpp - - - - "guiengine/event_handler.hpp" - "input/input.hpp" - "utils/no_copy.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\main_menu_screen.hpp - "guiengine/screen.hpp" - -1342755616 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\news_manager.cpp - "addons/news_manager.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "states_screens/addons_screen.hpp" - "states_screens/main_menu_screen.hpp" - "utils/string_utils.hpp" - "utils/time.hpp" - "utils/translation.hpp" - - -1306087408 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\request.cpp - "addons/request.hpp" - - "addons/addon.hpp" - -1307968040 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\addons\zip.cpp - - - - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "utils/string_utils.hpp" - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iwritefile.h - "IReferenceCounted.h" - "path.h" - -1348797130 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\animation_base.cpp - "animations/animation_base.hpp" - "animations/ipo.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - - -1340500962 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\animation_base.hpp - - "animations/ipo.hpp" - "tracks/track_object.hpp" - "utils/ptr_vector.hpp" - - -1335740930 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\ipo.hpp - - - - - "utils/no_copy.hpp" - "utils/vec3.hpp" - -1345686776 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_object.hpp - - - "items/item.hpp" - "utils/no_copy.hpp" - "utils/vec3.hpp" - - "graphics/lod_node.hpp" - -1353882294 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\lod_node.hpp - - - - - - -1351464880 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\billboard_animation.cpp - "animations/billboard_animation.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "io/file_manager.hpp" - - - - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\billboard_animation.hpp - - "animations/animation_base.hpp" - -1351552470 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\material_manager.hpp - "utils/no_copy.hpp" - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenemanager.h - "IReferenceCounted.h" - "irrArray.h" - "irrString.h" - "path.h" - "vector3d.h" - "dimension2d.h" - "SColor.h" - "ETerrainElements.h" - "ESceneNodeTypes.h" - "ESceneNodeAnimatorTypes.h" - "EMeshWriterEnums.h" - "SceneParameters.h" - "IGeometryCreator.h" - "ISkinnedMesh.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\eterrainelements.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\emeshwriterenums.h - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\sceneparameters.h - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\igeometrycreator.h - "IReferenceCounted.h" - "IMesh.h" - "IImage.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iskinnedmesh.h - "irrArray.h" - "IBoneSceneNode.h" - "IAnimatedMesh.h" - "SSkinMeshBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\sskinmeshbuffer.h - "IMeshBuffer.h" - "S3DVertex.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\icamerascenenode.h - "ISceneNode.h" - "IEventReceiver.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ibillboardscenenode.h - "ISceneNode.h" - -1335740930 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\ipo.cpp - "animations/ipo.hpp" - "io/xml_node.hpp" - - -1353380242 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\three_d_animation.cpp - "animations/three_d_animation.hpp" - - "audio/sfx_base.hpp" - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "graphics/mesh_tools.hpp" - "io/xml_node.hpp" - "modes/world.hpp" - "physics/kart_motion_state.hpp" - "physics/physics.hpp" - "physics/triangle_mesh.hpp" - "tracks/bezier_curve.hpp" - "utils/constants.hpp" - - - -1350346174 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\animations\three_d_animation.hpp - - - "btBulletDynamicsCommon.h" - "animations/animation_base.hpp" - "physics/user_pointer.hpp" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\btbulletdynamicscommon.h - "btBulletCollisionCommon.h" - "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" - "BulletDynamics/Dynamics/btSimpleDynamicsWorld.h" - "BulletDynamics/Dynamics/btRigidBody.h" - "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h" - "BulletDynamics/ConstraintSolver/btHingeConstraint.h" - "BulletDynamics/ConstraintSolver/btConeTwistConstraint.h" - "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h" - "BulletDynamics/ConstraintSolver/btSliderConstraint.h" - "BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h" - "BulletDynamics/ConstraintSolver/btUniversalConstraint.h" - "BulletDynamics/ConstraintSolver/btHinge2Constraint.h" - "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" - "BulletDynamics/Vehicle/btRaycastVehicle.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\btbulletcollisioncommon.h - "BulletCollision/CollisionDispatch/btCollisionWorld.h" - "BulletCollision/CollisionDispatch/btCollisionObject.h" - "BulletCollision/CollisionShapes/btBoxShape.h" - "BulletCollision/CollisionShapes/btSphereShape.h" - "BulletCollision/CollisionShapes/btCapsuleShape.h" - "BulletCollision/CollisionShapes/btCylinderShape.h" - "BulletCollision/CollisionShapes/btConeShape.h" - "BulletCollision/CollisionShapes/btStaticPlaneShape.h" - "BulletCollision/CollisionShapes/btConvexHullShape.h" - "BulletCollision/CollisionShapes/btTriangleMesh.h" - "BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h" - "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" - "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" - "BulletCollision/CollisionShapes/btTriangleMeshShape.h" - "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h" - "BulletCollision/CollisionShapes/btCompoundShape.h" - "BulletCollision/CollisionShapes/btTetrahedronShape.h" - "BulletCollision/CollisionShapes/btEmptyShape.h" - "BulletCollision/CollisionShapes/btMultiSphereShape.h" - "BulletCollision/CollisionShapes/btUniformScalingShape.h" - "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h" - "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h" - "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" - "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" - "BulletCollision/BroadphaseCollision/btAxisSweep3.h" - "BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h" - "BulletCollision/BroadphaseCollision/btDbvtBroadphase.h" - "LinearMath/btQuaternion.h" - "LinearMath/btTransform.h" - "LinearMath/btDefaultMotionState.h" - "LinearMath/btQuickprof.h" - "LinearMath/btIDebugDraw.h" - "LinearMath/btSerializer.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btcollisionworld.h - "LinearMath/btVector3.h" - "LinearMath/btTransform.h" - "btCollisionObject.h" - "btCollisionDispatcher.h" - "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" - "LinearMath/btAlignedObjectArray.h" - -1332208288 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btcollisionobject.h - - "LinearMath/btTransform.h" - "LinearMath/btMotionState.h" - "LinearMath/btAlignedAllocator.h" - "LinearMath/btAlignedObjectArray.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btmotionstate.h - "btTransform.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btcollisiondispatcher.h - "BulletCollision/BroadphaseCollision/btDispatcher.h" - "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" - "BulletCollision/CollisionDispatch/btManifoldResult.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "LinearMath/btAlignedObjectArray.h" - "btCollisionCreateFunc.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btdispatcher.h - "LinearMath/btScalar.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\narrowphasecollision\btpersistentmanifold.h - "LinearMath/btVector3.h" - "LinearMath/btTransform.h" - "btManifoldPoint.h" - "LinearMath/btAlignedAllocator.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\narrowphasecollision\btmanifoldpoint.h - "LinearMath/btVector3.h" - "LinearMath/btTransformUtil.h" - "physics_effects/base_level/solver/pfx_constraint_row.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\bttransformutil.h - "btTransform.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btmanifoldresult.h - "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" - "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" - "LinearMath/btTransform.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\narrowphasecollision\btdiscretecollisiondetectorinterface.h - "LinearMath/btTransform.h" - "LinearMath/btVector3.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btbroadphaseproxy.h - "LinearMath/btScalar.h" - "LinearMath/btVector3.h" - "LinearMath/btAlignedAllocator.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btcollisioncreatefunc.h - "LinearMath/btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btoverlappingpaircache.h - "btBroadphaseInterface.h" - "btBroadphaseProxy.h" - "btOverlappingPairCallback.h" - "LinearMath/btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btbroadphaseinterface.h - "btBroadphaseProxy.h" - "LinearMath/btVector3.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btoverlappingpaircallback.h - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btboxshape.h - "btPolyhedralConvexShape.h" - "btCollisionMargin.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "LinearMath/btVector3.h" - "LinearMath/btMinMax.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btpolyhedralconvexshape.h - "LinearMath/btMatrix3x3.h" - "btConvexInternalShape.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btconvexinternalshape.h - "btConvexShape.h" - "LinearMath/btAabbUtil2.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btconvexshape.h - "btCollisionShape.h" - "LinearMath/btVector3.h" - "LinearMath/btTransform.h" - "LinearMath/btMatrix3x3.h" - "btCollisionMargin.h" - "LinearMath/btAlignedAllocator.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btcollisionshape.h - "LinearMath/btTransform.h" - "LinearMath/btVector3.h" - "LinearMath/btMatrix3x3.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btcollisionmargin.h - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btaabbutil2.h - "btTransform.h" - "btVector3.h" - "btMinMax.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btsphereshape.h - "btConvexInternalShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btcapsuleshape.h - "btConvexInternalShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btcylindershape.h - "btBoxShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "LinearMath/btVector3.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btconeshape.h - "btConvexInternalShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btstaticplaneshape.h - "btConcaveShape.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btconcaveshape.h - "btCollisionShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "btTriangleCallback.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\bttrianglecallback.h - "LinearMath/btVector3.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btconvexhullshape.h - "btPolyhedralConvexShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "LinearMath/btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\bttrianglemesh.h - "btTriangleIndexVertexArray.h" - "LinearMath/btVector3.h" - "LinearMath/btAlignedObjectArray.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\bttriangleindexvertexarray.h - "btStridingMeshInterface.h" - "LinearMath/btAlignedObjectArray.h" - "LinearMath/btScalar.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btstridingmeshinterface.h - "LinearMath/btVector3.h" - "btTriangleCallback.h" - "btConcaveShape.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btconvextrianglemeshshape.h - "btPolyhedralConvexShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btbvhtrianglemeshshape.h - "btTriangleMeshShape.h" - "btOptimizedBvh.h" - "LinearMath/btAlignedAllocator.h" - "btTriangleInfoMap.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\bttrianglemeshshape.h - "btConcaveShape.h" - "btStridingMeshInterface.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btoptimizedbvh.h - "BulletCollision/BroadphaseCollision/btQuantizedBvh.h" - -1326072886 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btquantizedbvh.h - - - "LinearMath/btVector3.h" - "LinearMath/btAlignedAllocator.h" - "LinearMath/btAlignedAllocator.h" - "LinearMath/btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\bttriangleinfomap.h - "LinearMath/btHashMap.h" - "LinearMath/btSerializer.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\bthashmap.h - "btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btserializer.h - "btScalar.h" - "btStackAlloc.h" - "btHashMap.h" - - - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btstackalloc.h - "btScalar.h" - "btAlignedAllocator.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btscaledbvhtrianglemeshshape.h - "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btcompoundshape.h - "btCollisionShape.h" - "LinearMath/btVector3.h" - "LinearMath/btTransform.h" - "LinearMath/btMatrix3x3.h" - "btCollisionMargin.h" - "LinearMath/btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\bttetrahedronshape.h - "btPolyhedralConvexShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btemptyshape.h - "btConcaveShape.h" - "LinearMath/btVector3.h" - "LinearMath/btTransform.h" - "LinearMath/btMatrix3x3.h" - "btCollisionMargin.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btmultisphereshape.h - "btConvexInternalShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "LinearMath/btAlignedObjectArray.h" - "LinearMath/btAabbUtil2.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisionshapes\btuniformscalingshape.h - "btConvexShape.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btspherespherecollisionalgorithm.h - "btActivatingCollisionAlgorithm.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" - "btCollisionDispatcher.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btactivatingcollisionalgorithm.h - "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btcollisionalgorithm.h - "LinearMath/btScalar.h" - "LinearMath/btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btdefaultcollisionconfiguration.h - "btCollisionConfiguration.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\collisiondispatch\btcollisionconfiguration.h - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btsimplebroadphase.h - "btOverlappingPairCache.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btaxissweep3.h - "LinearMath/btVector3.h" - "btOverlappingPairCache.h" - "btBroadphaseInterface.h" - "btBroadphaseProxy.h" - "btOverlappingPairCallback.h" - "btDbvtBroadphase.h" - - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btdbvtbroadphase.h - "BulletCollision/BroadphaseCollision/btDbvt.h" - "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" - "LinearMath/btQuickprof.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btdbvt.h - "LinearMath/btAlignedObjectArray.h" - "LinearMath/btVector3.h" - "LinearMath/btTransform.h" - "LinearMath/btAabbUtil2.h" - - - - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btquickprof.h - - "btScalar.h" - "btAlignedAllocator.h" - - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletcollision\broadphasecollision\btmultisapbroadphase.h - "btBroadphaseInterface.h" - "LinearMath/btAlignedObjectArray.h" - "btOverlappingPairCache.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btdefaultmotionstate.h - "btMotionState.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\linearmath\btidebugdraw.h - "btVector3.h" - "btTransform.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\dynamics\btdiscretedynamicsworld.h - "btDynamicsWorld.h" - "LinearMath/btAlignedObjectArray.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\dynamics\btdynamicsworld.h - "BulletCollision/CollisionDispatch/btCollisionWorld.h" - "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btcontactsolverinfo.h - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\dynamics\btsimpledynamicsworld.h - "btDynamicsWorld.h" - -1332208288 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\dynamics\btrigidbody.h - "LinearMath/btAlignedObjectArray.h" - "LinearMath/btTransform.h" - "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" - "BulletCollision/CollisionDispatch/btCollisionObject.h" - - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btpoint2pointconstraint.h - "LinearMath/btVector3.h" - "btJacobianEntry.h" - "btTypedConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btjacobianentry.h - "LinearMath/btVector3.h" - "BulletDynamics/Dynamics/btRigidBody.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\bttypedconstraint.h - "LinearMath/btScalar.h" - "btSolverConstraint.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btsolverconstraint.h - "LinearMath/btVector3.h" - "LinearMath/btMatrix3x3.h" - "btJacobianEntry.h" - "btSolverBody.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btsolverbody.h - "LinearMath/btVector3.h" - "LinearMath/btMatrix3x3.h" - "BulletDynamics/Dynamics/btRigidBody.h" - "LinearMath/btAlignedAllocator.h" - "LinearMath/btTransformUtil.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\bthingeconstraint.h - "LinearMath/btVector3.h" - "btJacobianEntry.h" - "btTypedConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btconetwistconstraint.h - "LinearMath/btVector3.h" - "btJacobianEntry.h" - "btTypedConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btgeneric6dofconstraint.h - "LinearMath/btVector3.h" - "btJacobianEntry.h" - "btTypedConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btsliderconstraint.h - "LinearMath/btVector3.h" - "btJacobianEntry.h" - "btTypedConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btgeneric6dofspringconstraint.h - "LinearMath/btVector3.h" - "btTypedConstraint.h" - "btGeneric6DofConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btuniversalconstraint.h - "LinearMath/btVector3.h" - "btTypedConstraint.h" - "btGeneric6DofConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\bthinge2constraint.h - "LinearMath/btVector3.h" - "btTypedConstraint.h" - "btGeneric6DofSpringConstraint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btsequentialimpulseconstraintsolver.h - "btConstraintSolver.h" - "btContactConstraint.h" - "btSolverBody.h" - "btSolverConstraint.h" - "btTypedConstraint.h" - "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btconstraintsolver.h - "LinearMath/btScalar.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btcontactconstraint.h - "LinearMath/btVector3.h" - "btJacobianEntry.h" - "btTypedConstraint.h" - "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\vehicle\btraycastvehicle.h - "BulletDynamics/Dynamics/btRigidBody.h" - "BulletDynamics/ConstraintSolver/btTypedConstraint.h" - "btVehicleRaycaster.h" - "LinearMath/btAlignedObjectArray.h" - "btWheelInfo.h" - "BulletDynamics/Dynamics/btActionInterface.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\vehicle\btvehicleraycaster.h - "LinearMath/btVector3.h" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\vehicle\btwheelinfo.h - "LinearMath/btVector3.h" - "LinearMath/btTransform.h" - -1296684254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\dynamics\btactioninterface.h - "LinearMath/btScalar.h" - "btRigidBody.h" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\user_pointer.hpp - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\sfx_base.hpp - "audio/sfx_manager.hpp" - "utils/no_copy.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\mesh_tools.hpp - "utils/vec3.hpp" - -1353546430 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\world.hpp - - "modes/world_status.hpp" - "race/highscores.hpp" - "states_screens/race_gui_base.hpp" - "states_screens/state_manager.hpp" - "utils/random_generator.hpp" - -1349047860 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\world_status.hpp - "utils/cpp2011.h" - -1331764676 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\highscores.hpp - - - - "race/race_manager.hpp" - - -1351469346 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_gui_base.hpp - - - - - - - "utils/vec3.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\random_generator.hpp - - - -1332208288 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\kart_motion_state.hpp - - "LinearMath/btMotionState.h" - -1336427962 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\physics.hpp - - - "btBulletDynamicsCommon.h" - "physics/irr_debug_drawer.hpp" - "physics/stk_dynamics_world.hpp" - "physics/user_pointer.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\irr_debug_drawer.hpp - "btBulletDynamicsCommon.h" - "graphics/irr_driver.hpp" - "utils/vec3.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\stk_dynamics_world.hpp - "btBulletDynamicsCommon.h" - -1325974000 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\triangle_mesh.hpp - - "btBulletDynamicsCommon.h" - "physics/user_pointer.hpp" - "utils/aligned_array.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\bezier_curve.hpp - - "utils/aligned_array.hpp" - "utils/vec3.hpp" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imeshscenenode.h - "ISceneNode.h" - -1354588748 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music_information.cpp - "audio/music_information.hpp" - - - "audio/music_dummy.hpp" - "audio/music_ogg.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/string_utils.hpp" - -1332208288 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music_information.hpp - - - - "utils/no_copy.hpp" - "utils/leak_check.hpp" - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music_dummy.hpp - - "audio/music.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music.hpp - - "utils/no_copy.hpp" - -1332208288 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music_ogg.hpp - - - - - - "audio/music.hpp" - -1267382726 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\ogg\ogg.h - - -1267382726 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\ogg\os_types.h - <_G_config.h> - - - - - - -1331492407 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\ogg\config_types.h - - - - -1267382726 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\vorbis\vorbisfile.h - - "codec.h" - -1267382726 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\vorbis\codec.h - - -1327867730 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music_manager.cpp - "audio/music_manager.hpp" - - - - - - - "audio/music_ogg.hpp" - "audio/sfx_openal.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "utils/string_utils.hpp" - -1339892014 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music_manager.hpp - - - - "audio/music.hpp" - "audio/music_information.hpp" - "utils/no_copy.hpp" - -1267382722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\al\alc.h - -1342984842 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\sfx_openal.hpp - - - - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - -1355182222 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\music_ogg.cpp - "audio/music_ogg.hpp" - - - - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "config/user_config.hpp" - "utils/constants.hpp" - -1347950782 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\sfx_buffer.cpp - "audio/sfx_buffer.hpp" - "audio/sfx_manager.hpp" - "io/file_manager.hpp" - "utils/constants.hpp" - - - - - - - -1342987538 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\sfx_buffer.hpp - - - "utils/no_copy.hpp" - "utils/vec3.hpp" - - -1353380242 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\sfx_manager.cpp - "audio/dummy_sfx.hpp" - "audio/music_manager.hpp" - "audio/sfx_buffer.hpp" - - - - - - - - - - - - "audio/sfx_openal.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "race/race_manager.hpp" - "utils/constants.hpp" - - -1331051538 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\dummy_sfx.hpp - "audio/sfx_base.hpp" - -1342987538 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\audio\sfx_openal.cpp - "audio/sfx_openal.hpp" - "audio/sfx_buffer.hpp" - "race/race_manager.hpp" - - - - - - "config/user_config.hpp" - "io/file_manager.hpp" - -1350860794 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\challenge.cpp - "challenges/challenge.hpp" - - "challenges/challenge_data.hpp" - "io/xml_node.hpp" - "io/xml_writer.hpp" - "karts/kart_properties_manager.hpp" - "karts/kart_properties.hpp" - "race/grand_prix_manager.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/translation.hpp" - "utils/string_utils.hpp" - -1350860794 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\challenge.hpp - - - - - "race/race_manager.hpp" - "utils/no_copy.hpp" - "utils/translation.hpp" - -1353923982 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\challenge_data.hpp - - - - - "challenges/challenge.hpp" - "race/race_manager.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\io\xml_writer.hpp - - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\grand_prix_manager.hpp - - - "race/grand_prix_data.hpp" - -1353923982 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\challenge_data.cpp - "challenges/challenge_data.hpp" - - - "challenges/unlock_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/linear_world.hpp" - "race/grand_prix_data.hpp" - "race/grand_prix_manager.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - -1353976898 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\unlock_manager.hpp - - "config/user_config.hpp" - "challenges/challenge_data.hpp" - "challenges/game_slot.hpp" - "utils/no_copy.hpp" - "utils/ptr_vector.hpp" - - -1352721910 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\game_slot.hpp - - - - - "race/race_manager.hpp" - -1352942436 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\abstract_kart.hpp - "items/powerup_manager.hpp" - "karts/moveable.hpp" - "karts/controller/kart_control.hpp" - "race/race_manager.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\powerup_manager.hpp - - - "btBulletDynamicsCommon.h" - "utils/no_copy.hpp" - -1345419696 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\moveable.hpp - "btBulletDynamicsCommon.h" - "physics/kart_motion_state.hpp" - "physics/user_pointer.hpp" - "utils/no_copy.hpp" - "utils/vec3.hpp" - -1348037816 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\kart_control.hpp - "network/message.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\message.hpp - - - - - "btBulletDynamicsCommon.h" - "enet/enet.h" - "utils/vec3.hpp" - -1310445298 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\enet\enet.h - - "enet/win32.h" - "enet/unix.h" - "enet/types.h" - "enet/protocol.h" - "enet/list.h" - "enet/callbacks.h" - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\enet\win32.h - - - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\enet\unix.h - - - - - - -1235868408 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\enet\types.h - -1310445298 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\enet\protocol.h - "enet/types.h" - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\enet\list.h - - -1300395742 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\enet\include\enet\callbacks.h - - -1350253388 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\linear_world.hpp - - "modes/world_with_rank.hpp" - "tracks/track_sector.hpp" - "utils/aligned_array.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\world_with_rank.hpp - - "modes/world.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_sector.hpp - "utils/vec3.hpp" - -1352754310 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\game_slot.cpp - "challenges/game_slot.hpp" - "challenges/challenge.hpp" - "challenges/challenge_data.hpp" - "challenges/unlock_manager.hpp" - "io/xml_writer.hpp" - -1354331448 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\challenges\unlock_manager.cpp - "challenges/unlock_manager.hpp" - - - - - - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "config/player.hpp" - "config/user_config.hpp" - "challenges/challenge_data.hpp" - "io/file_manager.hpp" - "io/xml_writer.hpp" - "karts/kart_properties_manager.hpp" - "race/race_manager.hpp" - "tracks/track_manager.hpp" - "utils/string_utils.hpp" - -1323546480 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\device_config.cpp - - "config/device_config.hpp" - - -1323546480 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\device_config.hpp - "input/binding.hpp" - "input/input.hpp" - "utils/no_copy.hpp" - - - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\binding.hpp - - - "input/input.hpp" - "utils/no_copy.hpp" - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\skeymap.h - "Keycodes.h" - -1335394430 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\player.cpp - - "config/player.hpp" - "utils/string_utils.hpp" - - -1353277350 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\stk_config.cpp - "config/stk_config.hpp" - - - - "audio/music_information.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "items/item.hpp" - "karts/kart_properties.hpp" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\config\user_config.cpp - - - - - - "io/xml_writer.hpp" - "utils/ptr_vector.hpp" - "config/user_config.hpp" - "config/player.hpp" - "config/stk_config.hpp" - "guiengine/engine.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "race/race_manager.hpp" - "utils/ptr_vector.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1295574878 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\cbatchingmesh.cpp - "graphics/CBatchingMesh.hpp" - -1295574878 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\cbatchingmesh.hpp - "IMesh.h" - "SMeshBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\smeshbuffer.h - "CMeshBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\cmeshbuffer.h - "irrArray.h" - "IMeshBuffer.h" - -1353884374 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\camera.cpp - "graphics/camera.hpp" - - "audio/music_manager.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "karts/explosion_animation.hpp" - "karts/kart_properties.hpp" - "karts/skidding.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "utils/aligned_array.hpp" - "utils/constants.hpp" - - - -1352025364 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\explosion_animation.hpp - "karts/abstract_kart_animation.hpp" - "utils/vec3.hpp" - -1346907160 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\abstract_kart_animation.hpp - "utils/no_copy.hpp" - "utils/vec3.hpp" - - -1350510670 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\skidding.hpp - "karts/skidding_properties.hpp" - "karts/controller/kart_control.hpp" - "utils/leak_check.hpp" - "utils/no_copy.hpp" - - -1351145040 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\skidding_properties.hpp - "utils/leak_check.hpp" - "utils/no_copy.hpp" - - - -1352287398 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\explosion.cpp - "graphics/explosion.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "items/projectile_manager.hpp" - "race/race_manager.hpp" - "utils/vec3.hpp" - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\explosion.hpp - "graphics/hit_sfx.hpp" - "utils/no_copy.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\hit_sfx.hpp - "graphics/hit_effect.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\hit_effect.hpp - "utils/no_copy.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\projectile_manager.hpp - - "audio/sfx_manager.hpp" - "items/powerup_manager.hpp" - "utils/no_copy.hpp" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticlesystemscenenode.h - "ISceneNode.h" - "IParticleAnimatedMeshSceneNodeEmitter.h" - "IParticleBoxEmitter.h" - "IParticleCylinderEmitter.h" - "IParticleMeshEmitter.h" - "IParticleRingEmitter.h" - "IParticleSphereEmitter.h" - "IParticleAttractionAffector.h" - "IParticleFadeOutAffector.h" - "IParticleGravityAffector.h" - "IParticleRotationAffector.h" - "dimension2d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticleanimatedmeshscenenodeemitter.h - "IParticleEmitter.h" - "IAnimatedMeshSceneNode.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticleemitter.h - "IAttributeExchangingObject.h" - "SParticle.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\sparticle.h - "vector3d.h" - "dimension2d.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticleboxemitter.h - "IParticleEmitter.h" - "aabbox3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticlecylinderemitter.h - "IParticleEmitter.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticlemeshemitter.h - "IParticleEmitter.h" - "IMesh.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticleringemitter.h - "IParticleEmitter.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticlesphereemitter.h - "IParticleEmitter.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticleattractionaffector.h - "IParticleAffector.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticleaffector.h - "IAttributeExchangingObject.h" - "SParticle.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticlefadeoutaffector.h - "IParticleAffector.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticlegravityaffector.h - "IParticleAffector.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iparticlerotationaffector.h - "IParticleAffector.h" - -1310776618 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\hardware_skinning.cpp - "graphics/irr_driver.hpp" - "graphics/hardware_skinning.hpp" - - - - - - - -1310611524 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\hardware_skinning.hpp - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\igpuprogrammingservices.h - "EShaderTypes.h" - "EMaterialTypes.h" - "EPrimitiveTypes.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\eshadertypes.h - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imaterialrendererservices.h - "SMaterial.h" - "S3DVertex.h" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\hit_sfx.cpp - "graphics/hit_sfx.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "race/race_manager.hpp" - -1353456786 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\irr_driver.cpp - "graphics/irr_driver.hpp" - "config/user_config.hpp" - "graphics/camera.hpp" - "graphics/hardware_skinning.hpp" - "graphics/material_manager.hpp" - "graphics/particle_kind_manager.hpp" - "graphics/per_camera_node.hpp" - "graphics/referee.hpp" - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "io/file_manager.hpp" - "items/item_manager.hpp" - "items/powerup_manager.hpp" - "items/attachment_manager.hpp" - "items/projectile_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties_manager.hpp" - "main_loop.hpp" - "modes/profile_world.hpp" - "modes/world.hpp" - "physics/physics.hpp" - "states_screens/dialogs/confirm_resolution_dialog.hpp" - "states_screens/state_manager.hpp" - "utils/constants.hpp" - "utils/profiler.hpp" - - - - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\particle_kind_manager.hpp - "utils/no_copy.hpp" - "graphics/particle_kind.hpp" - - - -1330321938 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\particle_kind.hpp - "utils/no_copy.hpp" - - - -1306092592 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\per_camera_node.hpp - - - - - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\idummytransformationscenenode.h - "ISceneNode.h" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\referee.hpp - "irrlicht.h" - "utils/vec3.hpp" - -1351820084 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrlicht.h - "IrrCompileConfig.h" - "aabbox3d.h" - "CDynamicMeshBuffer.h" - "CIndexBuffer.h" - "CMeshBuffer.h" - "coreutil.h" - "CVertexBuffer.h" - "dimension2d.h" - "ECullingTypes.h" - "EDebugSceneTypes.h" - "EDriverFeatures.h" - "EDriverTypes.h" - "EGUIAlignment.h" - "EGUIElementTypes.h" - "EHardwareBufferFlags.h" - "EMaterialFlags.h" - "EMaterialTypes.h" - "EMeshWriterEnums.h" - "EMessageBoxFlags.h" - "ESceneNodeAnimatorTypes.h" - "ESceneNodeTypes.h" - "ETerrainElements.h" - "fast_atof.h" - "heapsort.h" - "IAnimatedMesh.h" - "IAnimatedMeshMD2.h" - "IAnimatedMeshMD3.h" - "IAnimatedMeshSceneNode.h" - "IAttributeExchangingObject.h" - "IAttributes.h" - "IBillboardSceneNode.h" - "IBillboardTextSceneNode.h" - "IBoneSceneNode.h" - "ICameraSceneNode.h" - "ICursorControl.h" - "IDummyTransformationSceneNode.h" - "IDynamicMeshBuffer.h" - "IEventReceiver.h" - "IFileList.h" - "IFileSystem.h" - "IGeometryCreator.h" - "IGPUProgrammingServices.h" - "IGUIButton.h" - "IGUICheckBox.h" - "IGUIColorSelectDialog.h" - "IGUIComboBox.h" - "IGUIContextMenu.h" - "IGUIEditBox.h" - "IGUIElement.h" - "IGUIElementFactory.h" - "IGUIEnvironment.h" - "IGUIFileOpenDialog.h" - "IGUIFont.h" - "IGUIFontBitmap.h" - "IGUIImage.h" - "IGUIInOutFader.h" - "IGUIListBox.h" - "IGUIMeshViewer.h" - "IGUIScrollBar.h" - "IGUISkin.h" - "IGUISpinBox.h" - "IGUISpriteBank.h" - "IGUIStaticText.h" - "IGUITabControl.h" - "IGUITable.h" - "IGUIToolbar.h" - "IGUIWindow.h" - "IGUITreeView.h" - "IImage.h" - "IImageLoader.h" - "IImageWriter.h" - "IIndexBuffer.h" - "ILightSceneNode.h" - "ILogger.h" - "IMaterialRenderer.h" - "IMaterialRendererServices.h" - "IMesh.h" - "IMeshBuffer.h" - "IMeshCache.h" - "IMeshLoader.h" - "IMeshManipulator.h" - "IMeshSceneNode.h" - "IMeshWriter.h" - "IColladaMeshWriter.h" - "IMetaTriangleSelector.h" - "IOSOperator.h" - "IParticleSystemSceneNode.h" - "IQ3LevelMesh.h" - "IQ3Shader.h" - "IReadFile.h" - "IReferenceCounted.h" - "irrArray.h" - "IRandomizer.h" - "IrrlichtDevice.h" - "irrList.h" - "irrMap.h" - "irrMath.h" - "irrString.h" - "irrTypes.h" - "path.h" - "irrXML.h" - "ISceneCollisionManager.h" - "ISceneLoader.h" - "ISceneManager.h" - "ISceneNode.h" - "ISceneNodeAnimator.h" - "ISceneNodeAnimatorCameraFPS.h" - "ISceneNodeAnimatorCameraMaya.h" - "ISceneNodeAnimatorCollisionResponse.h" - "ISceneNodeAnimatorFactory.h" - "ISceneNodeFactory.h" - "ISceneUserDataSerializer.h" - "IShaderConstantSetCallBack.h" - "IShadowVolumeSceneNode.h" - "ISkinnedMesh.h" - "ITerrainSceneNode.h" - "ITextSceneNode.h" - "ITexture.h" - "ITimer.h" - "ITriangleSelector.h" - "IVertexBuffer.h" - "IVideoDriver.h" - "IVideoModeList.h" - "IVolumeLightSceneNode.h" - "IWriteFile.h" - "IXMLReader.h" - "IXMLWriter.h" - "ILightManager.h" - "Keycodes.h" - "line2d.h" - "line3d.h" - "matrix4.h" - "plane3d.h" - "position2d.h" - "quaternion.h" - "rect.h" - "S3DVertex.h" - "SAnimatedMesh.h" - "SceneParameters.h" - "SColor.h" - "SExposedVideoData.h" - "SIrrCreationParameters.h" - "SKeyMap.h" - "SLight.h" - "SMaterial.h" - "SMesh.h" - "SMeshBuffer.h" - "SMeshBufferLightMap.h" - "SMeshBufferTangents.h" - "SParticle.h" - "SSharedMeshBuffer.h" - "SSkinMeshBuffer.h" - "SVertexIndex.h" - "SViewFrustum.h" - "triangle3d.h" - "vector2d.h" - "vector3d.h" - "SIrrCreationParameters.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\cdynamicmeshbuffer.h - "IDynamicMeshBuffer.h" - "CVertexBuffer.h" - "CIndexBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\idynamicmeshbuffer.h - "IMeshBuffer.h" - "IVertexBuffer.h" - "IIndexBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ivertexbuffer.h - "IReferenceCounted.h" - "irrArray.h" - "S3DVertex.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iindexbuffer.h - "IReferenceCounted.h" - "irrArray.h" - "SVertexIndex.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\cvertexbuffer.h - "IVertexBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\cindexbuffer.h - "IIndexBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ibillboardtextscenenode.h - "IBillboardSceneNode.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguibutton.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguicheckbox.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguicolorselectdialog.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguicombobox.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguicontextmenu.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguieditbox.h - "IGUIElement.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguielementfactory.h - "IReferenceCounted.h" - "EGUIElementTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguienvironment.h - "IReferenceCounted.h" - "IGUISkin.h" - "rect.h" - "EMessageBoxFlags.h" - "IEventReceiver.h" - "IXMLReader.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguifileopendialog.h - "IGUIElement.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguifont.h - "IReferenceCounted.h" - "SColor.h" - "rect.h" - "irrString.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguifontbitmap.h - "IGUIFont.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguiimage.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguiinoutfader.h - "IGUIElement.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguilistbox.h - "IGUIElement.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguimeshviewer.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguiscrollbar.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguispinbox.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguispritebank.h - "IReferenceCounted.h" - "irrArray.h" - "SColor.h" - "rect.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguitabcontrol.h - "IGUIElement.h" - "SColor.h" - "IGUISkin.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguitable.h - "IGUIElement.h" - "irrTypes.h" - "SColor.h" - "IGUISkin.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguitoolbar.h - "IGUIElement.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguitreeview.h - "IGUIElement.h" - "IGUIImageList.h" - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iguiimagelist.h - "IGUIElement.h" - "rect.h" - "irrTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iimageloader.h - "IReferenceCounted.h" - "IImage.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iimagewriter.h - "IReferenceCounted.h" - "irrString.h" - "coreutil.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ilightscenenode.h - "ISceneNode.h" - "SLight.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\slight.h - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imaterialrenderer.h - "IReferenceCounted.h" - "SMaterial.h" - "S3DVertex.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imeshcache.h - "IReferenceCounted.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imeshloader.h - "IReferenceCounted.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imeshmanipulator.h - "IReferenceCounted.h" - "vector3d.h" - "aabbox3d.h" - "matrix4.h" - "IAnimatedMesh.h" - "IMeshBuffer.h" - "SVertexManipulator.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\svertexmanipulator.h - "S3DVertex.h" - "SColor.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imeshwriter.h - "IReferenceCounted.h" - "EMeshWriterEnums.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\icolladameshwriter.h - "IMeshWriter.h" - "ISceneNode.h" - "IAnimatedMesh.h" - "SMaterial.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\imetatriangleselector.h - "ITriangleSelector.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iq3levelmesh.h - "IAnimatedMesh.h" - "IQ3Shader.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irandomizer.h - "IReferenceCounted.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\irrmap.h - "irrTypes.h" - "irrMath.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenecollisionmanager.h - "IReferenceCounted.h" - "vector3d.h" - "triangle3d.h" - "position2d.h" - "line3d.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\isceneloader.h - "IReferenceCounted.h" - "path.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenenodeanimatorcamerafps.h - "ISceneNodeAnimator.h" - "IEventReceiver.h" - "irrArray.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenenodeanimatorcameramaya.h - "ISceneNodeAnimator.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenenodeanimatorcollisionresponse.h - "ISceneNode.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenenodeanimatorfactory.h - "IReferenceCounted.h" - "ESceneNodeAnimatorTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iscenenodefactory.h - "IReferenceCounted.h" - "ESceneNodeTypes.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\isceneuserdataserializer.h - "IReferenceCounted.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ishadowvolumescenenode.h - "ISceneNode.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\iterrainscenenode.h - "ETerrainElements.h" - "ISceneNode.h" - "IDynamicMeshBuffer.h" - "irrArray.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\itextscenenode.h - "ISceneNode.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ivolumelightscenenode.h - "ISceneNode.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ixmlwriter.h - "IReferenceCounted.h" - "irrArray.h" - "irrString.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ilightmanager.h - "IReferenceCounted.h" - "irrArray.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\sanimatedmesh.h - "IAnimatedMesh.h" - "IMesh.h" - "aabbox3d.h" - "irrArray.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\sirrcreationparameters.h - "EDriverTypes.h" - "EDeviceTypes.h" - "dimension2d.h" - "ILogger.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\smesh.h - "IMesh.h" - "IMeshBuffer.h" - "aabbox3d.h" - "irrArray.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\smeshbufferlightmap.h - "CMeshBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\smeshbuffertangents.h - "CMeshBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\ssharedmeshbuffer.h - "irrArray.h" - "IMeshBuffer.h" - -1351735202 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\irrlicht\include\sviewfrustum.h - "plane3d.h" - "vector3d.h" - "line3d.h" - "aabbox3d.h" - "matrix4.h" - "IVideoDriver.h" - -1327883274 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\scalable_font.hpp - "IrrCompileConfig.h" - "IGUIFontBitmap.h" - "irrString.h" - "irrMap.h" - "IXMLReader.h" - "IReadFile.h" - "irrArray.h" - - "utils/leak_check.hpp" - -1345154562 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\item_manager.hpp - - - - - "items/item.hpp" - "utils/no_copy.hpp" - -1326693592 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\attachment_manager.hpp - "graphics/material.hpp" - "items/attachment.hpp" - "utils/no_copy.hpp" - -1346723668 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\attachment.hpp - "config/stk_config.hpp" - "items/attachment_plugin.hpp" - "utils/no_copy.hpp" - "utils/random_generator.hpp" - - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\attachment_plugin.hpp - "vector3d.h" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\main_loop.hpp - -1342325340 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\profile_world.hpp - "modes/standard_race.hpp" - -1336954800 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\standard_race.hpp - "modes/linear_world.hpp" - -1283994122 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\confirm_resolution_dialog.hpp - "config/player.hpp" - "guiengine/modaldialog.hpp" - -1310294022 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\profiler.hpp - - - - - - -1353882294 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\lod_node.cpp - "graphics/irr_driver.hpp" - "graphics/lod_node.hpp" - "graphics/hardware_skinning.hpp" - "graphics/material_manager.hpp" - "graphics/material.hpp" - "config/user_config.hpp" - - - - - -1343605576 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\material.cpp - "graphics/material.hpp" - - - "audio/sfx_base.hpp" - "audio/sfx_buffer.hpp" - "config/user_config.hpp" - "config/stk_config.hpp" - "guiengine/engine.hpp" - "graphics/irr_driver.hpp" - "graphics/particle_kind_manager.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "utils/string_utils.hpp" - "modes/world.hpp" - "tracks/track.hpp" - - - - - -1352873192 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\material_manager.cpp - "graphics/material_manager.hpp" - - - "config/user_config.hpp" - "graphics/material.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "modes/world.hpp" - "tracks/track.hpp" - "utils/string_utils.hpp" - - - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\mesh_tools.cpp - "graphics/mesh_tools.hpp" - - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\moving_texture.cpp - "graphics/moving_texture.hpp" - "io/xml_node.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\moving_texture.hpp - "utils/no_copy.hpp" - - - -1342836352 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\particle_emitter.cpp - "graphics/particle_emitter.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "graphics/particle_kind.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - - - - - - - -1342836352 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\particle_emitter.hpp - "utils/leak_check.hpp" - "utils/no_copy.hpp" - "utils/vec3.hpp" - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\particle_kind.cpp - "graphics/particle_kind.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "utils/constants.hpp" - - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\particle_kind_manager.cpp - "graphics/particle_kind_manager.hpp" - "io/file_manager.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - - -1306092592 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\per_camera_node.cpp - "graphics/irr_driver.hpp" - "graphics/per_camera_node.hpp" - - - - -1343434722 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\post_processing.cpp - - - "post_processing.hpp" - "irr_driver.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "race/race_manager.hpp" - -1355119936 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\rain.cpp - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "graphics/material.hpp" - "graphics/per_camera_node.hpp" - "graphics/rain.hpp" - "modes/world.hpp" - "states_screens/race_gui.hpp" - "utils/constants.hpp" - "utils/random_generator.hpp" - - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\rain.hpp - - -1351466360 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_gui.hpp - - - - "config/player.hpp" - "states_screens/race_gui_base.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\referee.cpp - "graphics/referee.hpp" - "graphics/irr_driver.hpp" - "graphics/mesh_tools.hpp" - "karts/abstract_kart.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\shadow.cpp - "graphics/shadow.hpp" - "graphics/irr_driver.hpp" - - - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\shadow.hpp - "utils/no_copy.hpp" - - -1341527878 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\show_curve.cpp - "graphics/show_curve.hpp" - "graphics/irr_driver.hpp" - "utils/vec3.hpp" - - - -1341527878 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\show_curve.hpp - "utils/leak_check.hpp" - "utils/no_copy.hpp" - - -1353762812 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\skid_marks.cpp - "graphics/skid_marks.hpp" - "config/stk_config.hpp" - "graphics/irr_driver.hpp" - "karts/controller/controller.hpp" - "karts/abstract_kart.hpp" - "karts/skidding.hpp" - "physics/btKart.hpp" - - - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\skid_marks.hpp - - - - "utils/no_copy.hpp" - "utils/vec3.hpp" - -1351598724 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\controller.hpp - - "input/input.hpp" - "states_screens/state_manager.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\btkart.hpp - "BulletDynamics/Dynamics/btRigidBody.h" - "BulletDynamics/ConstraintSolver/btTypedConstraint.h" - "physics/btKartRaycast.hpp" - "LinearMath/btAlignedObjectArray.h" - "BulletDynamics/Vehicle/btWheelInfo.h" - "BulletDynamics/Dynamics/btActionInterface.h" - -1336604272 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\btkartraycast.hpp - "BulletDynamics/Dynamics/btRigidBody.h" - "BulletDynamics/ConstraintSolver/btTypedConstraint.h" - "BulletDynamics/Vehicle/btVehicleRaycaster.h" - "LinearMath/btAlignedObjectArray.h" - "BulletDynamics/Vehicle/btWheelInfo.h" - "BulletDynamics/Dynamics/btActionInterface.h" - -1352267866 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\slip_stream.cpp - "graphics/slip_stream.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "karts/controller/controller.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/max_speed.hpp" - "modes/world.hpp" - "tracks/quad.hpp" - "utils/constants.hpp" - - - - -1343605576 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\slip_stream.hpp - - "graphics/moving_texture.hpp" - "utils/no_copy.hpp" - -1343605576 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\max_speed.hpp - -1326238814 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\stars.cpp - "graphics/stars.hpp" - - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "utils/constants.hpp" - - - -1307194176 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\graphics\stars.hpp - "utils/no_copy.hpp" - - - -1317187162 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\cguispritebank.cpp - "guiengine/CGUISpriteBank.h" - "IGUIEnvironment.h" - "IVideoDriver.h" - "ITexture.h" - - -1316715868 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\cguispritebank.h - "IrrCompileConfig.h" - - "IGUISpriteBank.h" - "utils/leak_check.hpp" - -1328470338 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\abstract_state_manager.cpp - "guiengine/abstract_state_manager.hpp" - - - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/screen.hpp" - "input/device_manager.hpp" - -1354838506 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\device_manager.hpp - "input/input_device.hpp" - "config/device_config.hpp" - "utils/no_copy.hpp" - "utils/ptr_vector.hpp" - - -1317787920 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\input_device.hpp - - "config/device_config.hpp" - "input/input.hpp" - "input/input_manager.hpp" - "states_screens/state_manager.hpp" - "utils/no_copy.hpp" - -1318723258 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\abstract_top_level_container.cpp - "graphics/irr_driver.hpp" - "guiengine/abstract_top_level_container.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widget.hpp" - "io/file_manager.hpp" - "utils/ptr_vector.hpp" - - - -1341546818 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\engine.cpp - "guiengine/engine.hpp" - "io/file_manager.hpp" - "graphics/irr_driver.hpp" - "input/input_manager.hpp" - "guiengine/event_handler.hpp" - "guiengine/modaldialog.hpp" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "guiengine/skin.hpp" - "guiengine/widget.hpp" - "modes/demo_world.hpp" - "modes/world.hpp" - "states_screens/race_gui_base.hpp" - - - - -1336954800 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\demo_world.hpp - "modes/profile_world.hpp" - -1348958790 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\event_handler.cpp - "guiengine/event_handler.hpp" - - - - "guiengine/abstract_state_manager.hpp" - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/screen.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/list_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "input/input_manager.hpp" - "modes/demo_world.hpp" - "modes/world.hpp" - "states_screens/state_manager.hpp" - "utils/profiler.hpp" - -1334782174 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\layout_manager.cpp - "guiengine/layout_manager.hpp" - - - - "graphics/irr_driver.hpp" - "guiengine/abstract_top_level_container.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widget.hpp" - "io/file_manager.hpp" - "utils/ptr_vector.hpp" - "utils/string_utils.hpp" - -1306712846 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\layout_manager.hpp - - - "utils/ptr_vector.hpp" - -1318723648 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\modaldialog.cpp - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/layout_manager.hpp" - "guiengine/modaldialog.hpp" - "guiengine/screen.hpp" - "guiengine/widget.hpp" - "input/input_manager.hpp" - "io/file_manager.hpp" - - -1334789996 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\scalable_font.cpp - "guiengine/scalable_font.hpp" - - - - - - "guiengine/engine.hpp" - "io/file_manager.hpp" - "utils/translation.hpp" - -1343337856 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\screen.cpp - "guiengine/screen.hpp" - "io/file_manager.hpp" - "guiengine/engine.hpp" - "guiengine/layout_manager.hpp" - "guiengine/modaldialog.hpp" - "guiengine/widget.hpp" - "modes/world.hpp" - "states_screens/state_manager.hpp" - - - -1343337856 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\screen_loader.cpp - "guiengine/screen.hpp" - "guiengine/engine.hpp" - "guiengine/widgets.hpp" - "utils/translation.hpp" - - - - -1352940972 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\skin.cpp - "guiengine/skin.hpp" - - - - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "guiengine/widgets.hpp" - "io/file_manager.hpp" - "states_screens/state_manager.hpp" - -1323303924 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widget.cpp - "guiengine/widget.hpp" - - - - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "io/file_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1321748148 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\cguieditbox.cpp - "CGUIEditBox.h" - "IGUISkin.h" - "IGUIEnvironment.h" - "IGUIFont.h" - "IVideoDriver.h" - "rect.h" - "Keycodes.h" - "graphics/irr_driver.hpp" - "utils/translation.hpp" - "utils/time.hpp" - -1321748148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\cguieditbox.h - "IrrCompileConfig.h" - "IGUIEditBox.h" - "irrArray.h" - "IOSOperator.h" - "utils/leak_check.hpp" - "utils/time.hpp" - -1323303924 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\bubble_widget.cpp - "guiengine/engine.hpp" - "guiengine/widgets/bubble_widget.hpp" - "utils/translation.hpp" - - - - -1306092592 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\button_widget.cpp - "guiengine/engine.hpp" - "guiengine/widgets/button_widget.hpp" - - - - -1323303924 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\check_box_widget.cpp - "guiengine/engine.hpp" - "guiengine/widgets/check_box_widget.hpp" - - - - -1318981168 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\dynamic_ribbon_widget.cpp - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "io/file_manager.hpp" - "states_screens/state_manager.hpp" - - - - -1323734284 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\icon_button_widget.cpp - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "io/file_manager.hpp" - "utils/translation.hpp" - - - - - - -1306092592 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\label_widget.cpp - "guiengine/widgets/label_widget.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/skin.hpp" - "utils/translation.hpp" - - - - - - -1323303924 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\list_widget.cpp - "graphics/irr_driver.hpp" - "guiengine/CGUISpriteBank.h" - "guiengine/engine.hpp" - "guiengine/widgets/list_widget.hpp" - "io/file_manager.hpp" - - - - - -1353289332 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\model_view_widget.cpp - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/widgets/model_view_widget.hpp" - -1306092592 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\progress_bar_widget.cpp - "guiengine/engine.hpp" - "guiengine/widgets/progress_bar_widget.hpp" - "utils/string_utils.hpp" - - - - - -1341546818 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\ribbon_widget.cpp - "guiengine/widgets/ribbon_widget.hpp" - - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/layout_manager.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets/button_widget.hpp" - "input/input_manager.hpp" - "io/file_manager.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - - - - -1341546818 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\spinner_widget.cpp - - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/widgets/spinner_widget.hpp" - "io/file_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - - - - -1323303924 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\guiengine\widgets\text_box_widget.cpp - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/widgets/text_box_widget.hpp" - "guiengine/widgets/CGUIEditBox.h" - "utils/ptr_vector.hpp" - "utils/translation.hpp" - - - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\binding.cpp - - "input/binding.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1354838506 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\device_manager.cpp - "input/device_manager.hpp" - - - "config/player.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "states_screens/kart_selection.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - "input/wiimote_manager.hpp" - -1325462772 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\wiimote_manager.hpp - - "IEventReceiver.h" - -1355006146 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\input_device.cpp - "config/device_config.hpp" - "guiengine/abstract_state_manager.hpp" - "input/input.hpp" - "input/input_device.hpp" - "karts/abstract_kart.hpp" - "karts/controller/player_controller.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "states_screens/state_manager.hpp" - -1351598724 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\player_controller.hpp - "config/player.hpp" - "karts/controller/controller.hpp" - -1351833528 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\input_manager.cpp - "input/input_manager.hpp" - "main_loop.hpp" - "guiengine/engine.hpp" - "guiengine/event_handler.hpp" - "guiengine/modaldialog.hpp" - "guiengine/screen.hpp" - "input/device_manager.hpp" - "input/input.hpp" - "karts/controller/controller.hpp" - "karts/abstract_kart.hpp" - "modes/demo_world.hpp" - "modes/profile_world.hpp" - "modes/world.hpp" - "physics/physics.hpp" - "race/history.hpp" - "replay/replay_recorder.hpp" - "states_screens/kart_selection.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/options_screen_input2.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - - - - - - - - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\history.hpp - - "LinearMath/btQuaternion.h" - "karts/controller/kart_control.hpp" - "utils/aligned_array.hpp" - "utils/vec3.hpp" - -1348037816 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\replay\replay_recorder.hpp - "karts/controller/kart_control.hpp" - "replay/replay_base.hpp" - - -1348037816 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\replay\replay_base.hpp - "LinearMath/btTransform.h" - "utils/no_copy.hpp" - - - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_input2.hpp - - - "guiengine/screen.hpp" - "states_screens/dialogs/message_dialog.hpp" - -1316715868 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\message_dialog.hpp - "config/player.hpp" - "guiengine/modaldialog.hpp" - "utils/leak_check.hpp" - -1326750260 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\input\wiimote_manager.cpp - "input/wiimote_manager.hpp" - "wiiuse/wiiuse.h" - "graphics/irr_driver.hpp" - "input/input_manager.hpp" - "input/device_manager.hpp" - "utils/string_utils.hpp" - -1352873192 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\io\file_manager.cpp - - "io/file_manager.hpp" - - - - - - - - - - - - - - - "btBulletDynamicsCommon.h" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "karts/kart_properties_manager.hpp" - "tracks/track_manager.hpp" - "utils/string_utils.hpp" - - -1350366706 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\io\xml_node.cpp - "io/file_manager.hpp" - "io/xml_node.hpp" - "utils/string_utils.hpp" - "utils/interpolation_array.hpp" - "utils/vec3.hpp" - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\io\xml_writer.cpp - "io/xml_writer.hpp" - - - - -1346810816 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\attachment.cpp - "items/attachment.hpp" - - "audio/sfx_base.hpp" - "config/stk_config.hpp" - "config/user_config.hpp" - "graphics/explosion.hpp" - "graphics/irr_driver.hpp" - "items/attachment_manager.hpp" - "items/projectile_manager.hpp" - "items/swatter.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/explosion_animation.hpp" - "karts/kart_properties.hpp" - "modes/three_strikes_battle.hpp" - "network/race_state.hpp" - "network/network_manager.hpp" - "utils/constants.hpp" - -1332974146 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\swatter.hpp - "config/stk_config.hpp" - "items/attachment_plugin.hpp" - "utils/no_copy.hpp" - "utils/random_generator.hpp" - - - -1336460836 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\three_strikes_battle.hpp - "modes/world_with_rank.hpp" - "states_screens/race_gui_base.hpp" - - - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_state.hpp - - "items/flyable.hpp" - "items/item.hpp" - "karts/abstract_kart.hpp" - "karts/controller/kart_control.hpp" - "modes/world.hpp" - "network/flyable_info.hpp" - "network/item_info.hpp" - "network/message.hpp" - "utils/aligned_array.hpp" - -1354485668 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\flyable.hpp - - "items/powerup_manager.hpp" - "karts/moveable.hpp" - "tracks/terrain_info.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\terrain_info.hpp - "utils/vec3.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\flyable_info.hpp - "network/message.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\item_info.hpp - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\network_manager.hpp - - - "enet/enet.h" - "network/remote_kart_info.hpp" - -1346723668 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\attachment_manager.cpp - "items/attachment_manager.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "io/file_manager.hpp" - -1354444158 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\bowling.cpp - "items/bowling.hpp" - "graphics/hit_sfx.hpp" - "graphics/material.hpp" - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "utils/random_generator.hpp" - -1354444158 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\bowling.hpp - - "items/flyable.hpp" - -1332978946 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\cake.cpp - "items/cake.hpp" - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "utils/constants.hpp" - "utils/random_generator.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\cake.hpp - - "items/flyable.hpp" - -1352033824 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\flyable.cpp - "items/flyable.hpp" - - - - "graphics/explosion.hpp" - "graphics/irr_driver.hpp" - "graphics/mesh_tools.hpp" - "graphics/stars.hpp" - "io/xml_node.hpp" - "items/projectile_manager.hpp" - "karts/abstract_kart.hpp" - "karts/explosion_animation.hpp" - "modes/world.hpp" - "network/flyable_info.hpp" - "physics/physics.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - -1350759068 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\item.cpp - "items/item.hpp" - - - "graphics/irr_driver.hpp" - "graphics/lod_node.hpp" - "karts/abstract_kart.hpp" - "modes/three_strikes_battle.hpp" - "modes/world.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - "utils/vec3.hpp" - -1348958790 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\item_manager.cpp - "items/item_manager.hpp" - - - - "config/stk_config.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "io/file_manager.hpp" - "karts/abstract_kart.hpp" - "modes/linear_world.hpp" - "network/network_manager.hpp" - "tracks/quad_graph.hpp" - "tracks/track.hpp" - "utils/string_utils.hpp" - - - -1350961780 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\plunger.cpp - "items/plunger.hpp" - "graphics/irr_driver.hpp" - "io/xml_node.hpp" - "items/rubber_band.hpp" - "items/projectile_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties.hpp" - "modes/world.hpp" - "physics/physical_object.hpp" - "physics/physics.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\plunger.hpp - - "items/flyable.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\rubber_band.hpp - "utils/no_copy.hpp" - "utils/vec3.hpp" - -1350346174 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\physical_object.hpp - - "btBulletDynamicsCommon.h" - "physics/user_pointer.hpp" - "tracks/track_object.hpp" - "utils/vec3.hpp" - -1344494250 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\powerup.cpp - "items/powerup.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "config/user_config.hpp" - "config/stk_config.hpp" - "items/attachment.hpp" - "items/item_manager.hpp" - "items/projectile_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties.hpp" - "modes/world.hpp" - "network/network_manager.hpp" - "network/race_state.hpp" - "physics/triangle_mesh.hpp" - "tracks/track.hpp" - "utils/string_utils.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\powerup.hpp - "items/powerup_manager.hpp" - "utils/no_copy.hpp" - "utils/random_generator.hpp" - -1326693592 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\powerup_manager.cpp - "items/powerup_manager.hpp" - - - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "items/bowling.hpp" - "items/cake.hpp" - "items/plunger.hpp" - "items/rubber_ball.hpp" - "modes/world.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\rubber_ball.hpp - - "items/flyable.hpp" - "tracks/track_sector.hpp" - -1352321370 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\projectile_manager.cpp - "items/projectile_manager.hpp" - "graphics/explosion.hpp" - "graphics/hit_effect.hpp" - "items/bowling.hpp" - "items/cake.hpp" - "items/plunger.hpp" - "items/powerup_manager.hpp" - "items/powerup.hpp" - "items/rubber_ball.hpp" - "network/network_manager.hpp" - "network/race_state.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\rubber_ball.cpp - "items/rubber_ball.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "items/attachment.hpp" - "items/projectile_manager.hpp" - "karts/abstract_kart.hpp" - "modes/linear_world.hpp" - "physics/btKart.hpp" - "physics/triangle_mesh.hpp" - "tracks/track.hpp" - -1343605576 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\rubber_band.cpp - "items/rubber_band.hpp" - - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "items/plunger.hpp" - "items/projectile_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/max_speed.hpp" - "modes/world.hpp" - "physics/physics.hpp" - "race/race_manager.hpp" - "utils/string_utils.hpp" - - -1354147526 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\items\swatter.cpp - "items/swatter.hpp" - "audio/music_manager.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "graphics/explosion.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "items/attachment.hpp" - "items/projectile_manager.hpp" - "karts/controller/controller.hpp" - "karts/explosion_animation.hpp" - "karts/kart_properties.hpp" - "modes/world.hpp" - "karts/abstract_kart.hpp" - -1333321062 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\abstract_kart.cpp - "karts/abstract_kart.hpp" - "items/powerup.hpp" - "karts/abstract_kart_animation.hpp" - "karts/kart_model.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - -1352267866 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\abstract_kart_animation.cpp - "karts/abstract_kart_animation.hpp" - "graphics/slip_stream.hpp" - "karts/abstract_kart.hpp" - "karts/skidding.hpp" - "modes/world.hpp" - "physics/physics.hpp" - -1346907202 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\cannon_animation.cpp - "karts/cannon_animation.hpp" - "animations/animation_base.hpp" - "animations/ipo.hpp" - "animations/three_d_animation.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "modes/world.hpp" - "LinearMath/btTransform.h" - -1335478666 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\cannon_animation.hpp - "karts/abstract_kart_animation.hpp" - "utils/vec3.hpp" - -1351598724 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\ai_base_controller.cpp - "karts/controller/ai_base_controller.hpp" - - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/skidding_properties.hpp" - "karts/controller/ai_properties.hpp" - "modes/linear_world.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - -1351598724 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\ai_base_controller.hpp - "karts/controller/controller.hpp" - "states_screens/state_manager.hpp" - -1351598724 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\ai_properties.hpp - "race/race_manager.hpp" - "utils/interpolation_array.hpp" - - - -1351598724 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\ai_properties.cpp - "karts/controller/ai_properties.hpp" - "io/xml_node.hpp" - "utils/string_utils.hpp" - -1339256898 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\controller.cpp - "karts/controller/controller.hpp" - "karts/abstract_kart.hpp" - -1352855280 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\end_controller.cpp - "karts/controller/end_controller.hpp" - "irrlicht.h" - - - - - "graphics/irr_driver.hpp" - "karts/abstract_kart.hpp" - "karts/max_speed.hpp" - "karts/rescue_animation.hpp" - "modes/linear_world.hpp" - "network/network_manager.hpp" - "race/race_manager.hpp" - "states_screens/race_result_gui.hpp" - "tracks/quad_graph.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - -1339256898 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\end_controller.hpp - "karts/controller/ai_base_controller.hpp" - -1334013518 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\rescue_animation.hpp - "karts/abstract_kart_animation.hpp" - "utils/vec3.hpp" - -1350759706 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_result_gui.hpp - "states_screens/race_gui_base.hpp" - - - "guiengine/screen.hpp" - "states_screens/state_manager.hpp" - -1353291572 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\player_controller.cpp - "karts/controller/player_controller.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "config/player.hpp" - "config/stk_config.hpp" - "graphics/camera.hpp" - "graphics/irr_driver.hpp" - "input/input_manager.hpp" - "items/attachment.hpp" - "items/item.hpp" - "items/powerup.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/skidding.hpp" - "karts/rescue_animation.hpp" - "modes/world.hpp" - "race/history.hpp" - "states_screens/race_gui_base.hpp" - "utils/constants.hpp" - "utils/translation.hpp" - -1354167616 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\skidding_ai.cpp - "karts/controller/skidding_ai.hpp" - "irrlicht.h" - - - - - - "graphics/irr_driver.hpp" - "graphics/show_curve.hpp" - "graphics/slip_stream.hpp" - "karts/abstract_kart.hpp" - "karts/controller/kart_control.hpp" - "karts/controller/ai_properties.hpp" - "karts/kart_properties.hpp" - "karts/max_speed.hpp" - "karts/rescue_animation.hpp" - "karts/skidding.hpp" - "karts/skidding_properties.hpp" - "items/attachment.hpp" - "items/item_manager.hpp" - "items/powerup.hpp" - "modes/linear_world.hpp" - "modes/profile_world.hpp" - "network/network_manager.hpp" - "race/race_manager.hpp" - "tracks/quad_graph.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - -1351548378 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\controller\skidding_ai.hpp - "karts/controller/ai_base_controller.hpp" - "race/race_manager.hpp" - "tracks/graph_node.hpp" - "utils/random_generator.hpp" - -1352025364 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\explosion_animation.cpp - "karts/explosion_animation.hpp" - "audio/sfx_manager.hpp" - "items/attachment.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "modes/world.hpp" - "tracks/track.hpp" - -1331766932 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\ghost_kart.cpp - "karts/ghost_kart.hpp" - "modes/world.hpp" - "LinearMath/btQuaternion.h" - -1331766932 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\ghost_kart.hpp - "karts/kart.hpp" - "replay/replay_base.hpp" - "LinearMath/btTransform.h" - - -1354485628 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart.hpp - "LinearMath/btTransform.h" - "items/powerup.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "tracks/terrain_info.hpp" - "utils/no_copy.hpp" - -1354485568 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart.cpp - "karts/kart.hpp" - - - - - - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "audio/sfx_base.hpp" - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "graphics/camera.hpp" - "graphics/material_manager.hpp" - "graphics/particle_emitter.hpp" - "graphics/particle_kind.hpp" - "graphics/particle_kind_manager.hpp" - "graphics/rain.hpp" - "graphics/shadow.hpp" - "graphics/skid_marks.hpp" - "graphics/slip_stream.hpp" - "graphics/stars.hpp" - "guiengine/scalable_font.hpp" - "karts/explosion_animation.hpp" - "karts/kart_gfx.hpp" - "karts/rescue_animation.hpp" - "modes/overworld.hpp" - "modes/world.hpp" - "io/file_manager.hpp" - "items/attachment.hpp" - "items/item_manager.hpp" - "karts/controller/end_controller.hpp" - "karts/abstract_kart_animation.hpp" - "karts/kart_model.hpp" - "karts/kart_properties_manager.hpp" - "karts/max_speed.hpp" - "karts/skidding.hpp" - "modes/linear_world.hpp" - "network/race_state.hpp" - "network/network_manager.hpp" - "physics/btKart.hpp" - "physics/btKartRaycast.hpp" - "physics/btUprightConstraint.hpp" - "physics/physics.hpp" - "race/history.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/constants.hpp" - - -1332188472 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_gfx.hpp - - - -1348958790 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\overworld.hpp - - "modes/linear_world.hpp" - "utils/aligned_array.hpp" - "LinearMath/btTransform.h" - -1317187162 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\btuprightconstraint.hpp - "LinearMath/btVector3.h" - "BulletDynamics/ConstraintSolver/btJacobianEntry.h" - "BulletDynamics/ConstraintSolver/btTypedConstraint.h" - -1347573328 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_gfx.cpp - "karts/kart_gfx.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "graphics/particle_emitter.hpp" - "graphics/particle_kind.hpp" - "graphics/particle_kind_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/skidding.hpp" - "physics/btKart.hpp" - - -1353882294 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_model.cpp - "karts/kart_model.hpp" - - - "config/stk_config.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "graphics/lod_node.hpp" - "graphics/mesh_tools.hpp" - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "physics/btKart.hpp" - "utils/constants.hpp" - -1352942436 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_properties.cpp - "karts/kart_properties.hpp" - - - - "config/stk_config.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "io/file_manager.hpp" - "karts/controller/ai_properties.hpp" - "karts/kart_model.hpp" - "karts/skidding_properties.hpp" - "modes/world.hpp" - "io/xml_node.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1342478208 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_properties_manager.cpp - "karts/kart_properties_manager.hpp" - - - - - - "challenges/unlock_manager.hpp" - "config/stk_config.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "io/file_manager.hpp" - "karts/kart_properties.hpp" - "utils/string_utils.hpp" - -1346910482 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_with_stats.cpp - "karts/kart_with_stats.hpp" - "karts/explosion_animation.hpp" - "karts/rescue_animation.hpp" - "items/item.hpp" - "modes/linear_world.hpp" - -1346910482 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\kart_with_stats.hpp - "karts/kart.hpp" - -1352267866 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\max_speed.cpp - "karts/max_speed.hpp" - - - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "physics/btKart.hpp" - -1332693682 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\moveable.cpp - "karts/moveable.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "modes/world.hpp" - "tracks/track.hpp" - "ISceneNode.h" - -1334754858 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\rescue_animation.cpp - "karts/rescue_animation.hpp" - "graphics/referee.hpp" - "items/attachment.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "modes/three_strikes_battle.hpp" - "modes/world.hpp" - "physics/physics.hpp" - "ISceneNode.h" - -1351145040 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\skidding.cpp - "karts/skidding.hpp" - "graphics/show_curve.hpp" - "karts/kart.hpp" - "karts/kart_gfx.hpp" - "karts/kart_properties.hpp" - "karts/max_speed.hpp" - "karts/controller/controller.hpp" - "modes/world.hpp" - "physics/btKart.hpp" - "tracks/track.hpp" - -1351145040 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\karts\skidding_properties.cpp - "karts/skidding_properties.hpp" - "io/xml_node.hpp" - - -1353031056 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\main.cpp - - - - - - - - - - - - - - "main_loop.hpp" - "addons/addons_manager.hpp" - "addons/inetwork_http.hpp" - "addons/news_manager.hpp" - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "challenges/unlock_manager.hpp" - "config/stk_config.hpp" - "config/user_config.hpp" - "config/player.hpp" - "graphics/hardware_skinning.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "graphics/particle_kind_manager.hpp" - "graphics/referee.hpp" - "guiengine/engine.hpp" - "guiengine/event_handler.hpp" - "input/input_manager.hpp" - "input/device_manager.hpp" - "input/wiimote_manager.hpp" - "io/file_manager.hpp" - "items/attachment_manager.hpp" - "items/item_manager.hpp" - "items/projectile_manager.hpp" - "karts/controller/ai_base_controller.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/demo_world.hpp" - "modes/profile_world.hpp" - "network/network_manager.hpp" - "race/grand_prix_manager.hpp" - "race/highscore_manager.hpp" - "race/history.hpp" - "race/race_manager.hpp" - "replay/replay_play.hpp" - "replay/replay_recorder.hpp" - "states_screens/story_mode_lobby.hpp" - "states_screens/state_manager.hpp" - "states_screens/dialogs/message_dialog.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "tutorial/tutorial_manager.hpp" - "utils/constants.hpp" - "utils/leak_check.hpp" - "utils/translation.hpp" - -1353288254 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\highscore_manager.hpp - - - - "race/highscores.hpp" - -1331766932 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\replay\replay_play.hpp - "replay/replay_base.hpp" - "utils/ptr_vector.hpp" - - - -1323197328 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\story_mode_lobby.hpp - - "guiengine/screen.hpp" - "states_screens/dialogs/enter_player_name_dialog.hpp" - -1335397428 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\enter_player_name_dialog.hpp - - "guiengine/modaldialog.hpp" - "guiengine/widgets/text_box_widget.hpp" - -1294878424 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tutorial\tutorial_manager.hpp - - "tutorial/tutorial.hpp" - "tutorial/tutorial_data.hpp" - "utils/no_copy.hpp" - - -1306089086 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tutorial\tutorial.hpp - - - - - "utils/no_copy.hpp" - -1294878424 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tutorial\tutorial_data.hpp - - - - - "tutorial/tutorial.hpp" - "race/race_manager.hpp" - -1333291706 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\main_loop.cpp - "main_loop.hpp" - - "audio/music_manager.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "guiengine/engine.hpp" - "input/input_manager.hpp" - "input/wiimote_manager.hpp" - "modes/profile_world.hpp" - "modes/world.hpp" - "network/network_manager.hpp" - "race/race_manager.hpp" - "states_screens/state_manager.hpp" - "utils/profiler.hpp" - -1352938176 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\cutscene_world.cpp - "modes/cutscene_world.hpp" - - - - "animations/animation_base.hpp" - "audio/music_manager.hpp" - "challenges/game_slot.hpp" - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_model.hpp" - "karts/kart_properties.hpp" - "modes/overworld.hpp" - "physics/physics.hpp" - "states_screens/credits.hpp" - "states_screens/cutscene_gui.hpp" - "states_screens/kart_selection.hpp" - "states_screens/main_menu_screen.hpp" - "tracks/track.hpp" - "tracks/track_object.hpp" - "tracks/track_object_manager.hpp" - "utils/constants.hpp" - "utils/ptr_vector.hpp" - -1349050308 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\cutscene_world.hpp - "modes/world_with_rank.hpp" - "states_screens/race_gui_base.hpp" - - - - -1347581018 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\credits.hpp - - "audio/music_manager.hpp" - "guiengine/screen.hpp" - "utils/ptr_vector.hpp" - -1340577730 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\cutscene_gui.hpp - - - - "config/player.hpp" - "states_screens/race_gui_base.hpp" - "utils/cpp2011.h" - -1352033824 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_object_manager.hpp - "physics/physical_object.hpp" - "tracks/track_object.hpp" - "utils/ptr_vector.hpp" - - - - -1353069596 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\demo_world.cpp - "modes/demo_world.hpp" - "guiengine/modaldialog.hpp" - "input/device_manager.hpp" - "input/input_manager.hpp" - "network/network_manager.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - -1339190040 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\follow_the_leader.cpp - "modes/follow_the_leader.hpp" - "audio/music_manager.hpp" - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "items/powerup_manager.hpp" - "karts/abstract_kart.hpp" - "states_screens/race_gui_base.hpp" - "tracks/track.hpp" - "utils/translation.hpp" - - -1337127694 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\follow_the_leader.hpp - "modes/linear_world.hpp" - -1292540234 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\game_tutorial.cpp - "modes/game_tutorial.hpp" - -1292540664 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\game_tutorial.hpp - "modes/linear_world.hpp" - -1354190374 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\linear_world.cpp - "modes/linear_world.hpp" - - "audio/music_manager.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties.hpp" - "network/network_manager.hpp" - "physics/physics.hpp" - "race/history.hpp" - "states_screens/race_gui_base.hpp" - "tracks/track_sector.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1355090260 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\overworld.cpp - "audio/music_manager.hpp" - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "input/device_manager.hpp" - "input/input.hpp" - "input/input_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "karts/rescue_animation.hpp" - "modes/overworld.hpp" - "physics/physics.hpp" - "network/network_manager.hpp" - "states_screens/dialogs/select_challenge.hpp" - "states_screens/kart_selection.hpp" - "states_screens/race_gui_overworld.hpp" - "tracks/track.hpp" - -1327716404 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\select_challenge.hpp - "guiengine/event_handler.hpp" - "guiengine/modaldialog.hpp" - -1353844224 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_gui_overworld.hpp - - - - - "config/player.hpp" - "states_screens/race_gui_base.hpp" - -1347234356 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\profile_world.cpp - "modes/profile_world.hpp" - "main_loop.hpp" - "graphics/camera.hpp" - "graphics/irr_driver.hpp" - "karts/kart_with_stats.hpp" - "karts/controller/controller.hpp" - "tracks/track.hpp" - - -1351207030 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\standard_race.cpp - "modes/standard_race.hpp" - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "items/powerup_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - -1341546818 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\three_strikes_battle.cpp - "modes/three_strikes_battle.hpp" - - - "audio/music_manager.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_model.hpp" - "karts/kart_properties.hpp" - "physics/physics.hpp" - "states_screens/race_gui_base.hpp" - "tracks/track.hpp" - "tracks/track_object_manager.hpp" - "utils/constants.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\tutorial_race.cpp - "modes/tutorial_race.hpp" - "audio/music_manager.hpp" - "tutorial/tutorial_manager.hpp" - "config/user_config.hpp" - "karts/abstract_kart.hpp" - "items/powerup_manager.hpp" - "states_screens/race_gui_base.hpp" - "tracks/track.hpp" - "utils/translation.hpp" - -1316137778 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\tutorial_race.hpp - "modes/linear_world.hpp" - "states_screens/race_gui_base.hpp" - -1353546430 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\world.cpp - "modes/world.hpp" - - - - - - "audio/music_manager.hpp" - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "graphics/camera.hpp" - "graphics/hardware_skinning.hpp" - "io/file_manager.hpp" - "items/projectile_manager.hpp" - "karts/controller/player_controller.hpp" - "karts/controller/end_controller.hpp" - "karts/controller/skidding_ai.hpp" - "karts/kart.hpp" - "karts/kart_properties_manager.hpp" - "modes/profile_world.hpp" - "network/network_manager.hpp" - "network/race_state.hpp" - "physics/btKart.hpp" - "physics/physics.hpp" - "physics/triangle_mesh.hpp" - "race/highscore_manager.hpp" - "race/history.hpp" - "race/race_manager.hpp" - "replay/replay_play.hpp" - "replay/replay_recorder.hpp" - "states_screens/dialogs/race_paused_dialog.hpp" - "states_screens/race_gui_base.hpp" - "states_screens/race_gui.hpp" - "states_screens/state_manager.hpp" - "states_screens/minimal_race_gui.hpp" - "states_screens/race_result_gui.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/constants.hpp" - "utils/profiler.hpp" - "utils/translation.hpp" - "utils/string_utils.hpp" - -1283994122 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\race_paused_dialog.hpp - "guiengine/modaldialog.hpp" - -1351466360 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\minimal_race_gui.hpp - - - - - - "config/player.hpp" - "states_screens/race_gui_base.hpp" - -1339875682 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\world_status.cpp - "modes/world_status.hpp" - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "audio/sfx_base.hpp" - "config/stk_config.hpp" - "graphics/irr_driver.hpp" - "guiengine/modaldialog.hpp" - "karts/abstract_kart.hpp" - "modes/world.hpp" - "tracks/track.hpp" - "network/network_manager.hpp" - "states_screens/dialogs/race_over_dialog.hpp" - - -1301672798 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\race_over_dialog.hpp - "guiengine/modaldialog.hpp" - -1350253388 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\modes\world_with_rank.cpp - "modes/world_with_rank.hpp" - "karts/abstract_kart.hpp" - "race/history.hpp" - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\connect_message.cpp - "network/connect_message.hpp" - - - - - "config/user_config.hpp" - "config/player.hpp" - "karts/kart_properties_manager.hpp" - "states_screens/state_manager.hpp" - "tracks/track_manager.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\connect_message.hpp - - "network/message.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\kart_control_message.cpp - "network/kart_control_message.hpp" - "karts/controller/controller.hpp" - "modes/world.hpp" - "network/network_kart.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\kart_control_message.hpp - "network/message.hpp" - -1330321938 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\network_kart.hpp - "karts/kart.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\kart_update_message.cpp - "network/kart_update_message.hpp" - "karts/abstract_kart.hpp" - "modes/world.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\kart_update_message.hpp - "network/message.hpp" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\message.cpp - "network/message.hpp" - - - - - -1331088102 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\network_kart.cpp - "network/network_manager.hpp" - "network/network_kart.hpp" - -1338856054 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\network_manager.cpp - "network/network_manager.hpp" - "config/stk_config.hpp" - "config/user_config.hpp" - "karts/kart_properties_manager.hpp" - "modes/world.hpp" - "network/connect_message.hpp" - "network/character_info_message.hpp" - "network/character_selected_message.hpp" - "network/race_info_message.hpp" - "network/race_start_message.hpp" - "network/world_loaded_message.hpp" - "network/race_state.hpp" - "network/kart_control_message.hpp" - "network/character_confirm_message.hpp" - "network/race_result_message.hpp" - "network/race_result_ack_message.hpp" - "race/race_manager.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\character_info_message.hpp - "karts/kart_properties_manager.hpp" - "network/message.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\character_selected_message.hpp - "network/message.hpp" - "network/remote_kart_info.hpp" - "race/race_manager.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_info_message.hpp - - "network/message.hpp" - "network/remote_kart_info.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_start_message.hpp - "network/message.hpp" - "network/remote_kart_info.hpp" - "race/race_manager.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\world_loaded_message.hpp - "network/message.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\character_confirm_message.hpp - - "network/message.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_result_message.hpp - - "network/message.hpp" - -1323197016 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_result_ack_message.hpp - - "network/message.hpp" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_info_message.cpp - "network/race_info_message.hpp" - "race/grand_prix_manager.hpp" - "race/race_manager.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_result_message.cpp - "network/race_result_message.hpp" - "karts/abstract_kart.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - -1352671014 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\network\race_state.cpp - "network/race_state.hpp" - "items/item_manager.hpp" - "items/powerup.hpp" - "items/projectile_manager.hpp" - "karts/rescue_animation.hpp" - "modes/world.hpp" - "network/network_manager.hpp" - "physics/physics.hpp" - -1354482980 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\btkart.cpp - "LinearMath/btVector3.h" - "btKart.hpp" - "BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h" - "BulletDynamics/ConstraintSolver/btJacobianEntry.h" - "LinearMath/btQuaternion.h" - "BulletDynamics/Dynamics/btDynamicsWorld.h" - "BulletDynamics/Vehicle/btVehicleRaycaster.h" - "BulletDynamics/Vehicle/btWheelInfo.h" - "LinearMath/btMinMax.h" - "LinearMath/btIDebugDraw.h" - "BulletDynamics/ConstraintSolver/btContactConstraint.h" - "karts/kart.hpp" - -1320655912 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\lib\bullet\src\bulletdynamics\constraintsolver\btsolve2linearconstraint.h - "LinearMath/btMatrix3x3.h" - "LinearMath/btVector3.h" - -1336604272 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\btkartraycast.cpp - "LinearMath/btVector3.h" - "btKartRaycast.hpp" - "BulletCollision/CollisionDispatch/btCollisionWorld.h" - "BulletDynamics/Dynamics/btDynamicsWorld.h" - "modes/world.hpp" - "physics/triangle_mesh.hpp" - "tracks/track.hpp" - -1317187162 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\btuprightconstraint.cpp - "physics/btUprightConstraint.hpp" - - - "BulletDynamics/Dynamics/btRigidBody.h" - "LinearMath/btTransformUtil.h" - "karts/kart.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\irr_debug_drawer.cpp - "physics/irr_debug_drawer.hpp" - "karts/abstract_kart.hpp" - "modes/world.hpp" - - -1353893524 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\physical_object.cpp - "physics/physical_object.hpp" - - - "graphics/irr_driver.hpp" - "graphics/mesh_tools.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "modes/world.hpp" - "physics/physics.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - - - -1352938236 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\physics.cpp - "physics/physics.hpp" - "animations/three_d_animation.hpp" - "config/user_config.hpp" - "karts/kart_properties.hpp" - "karts/rescue_animation.hpp" - "network/race_state.hpp" - "graphics/stars.hpp" - "karts/explosion_animation.hpp" - "physics/btKart.hpp" - "physics/btUprightConstraint.hpp" - "physics/irr_debug_drawer.hpp" - "physics/physical_object.hpp" - "physics/stk_dynamics_world.hpp" - "physics/triangle_mesh.hpp" - "tracks/track.hpp" - -1353277350 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\physics\triangle_mesh.cpp - "physics/triangle_mesh.hpp" - "btBulletDynamicsCommon.h" - "modes/world.hpp" - "physics/physics.hpp" - "utils/constants.hpp" - "utils/time.hpp" - - -1354018156 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\grand_prix_data.cpp - "race/grand_prix_data.hpp" - "challenges/unlock_manager.hpp" - "io/file_manager.hpp" - "tracks/track_manager.hpp" - "tracks/track.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\grand_prix_manager.cpp - "race/grand_prix_manager.hpp" - - "io/file_manager.hpp" - "utils/string_utils.hpp" - -1353887502 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\highscore_manager.cpp - "race/highscore_manager.hpp" - - - "config/user_config.hpp" - "io/file_manager.hpp" - "io/xml_writer.hpp" - "race/race_manager.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1331764676 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\highscores.cpp - "race/highscores.hpp" - - - "io/xml_node.hpp" - "io/xml_writer.hpp" - "race/race_manager.hpp" - -1342746856 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\history.cpp - "race/history.hpp" - - "io/file_manager.hpp" - "modes/world.hpp" - "karts/abstract_kart.hpp" - "physics/physics.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - -1354018156 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\race\race_manager.cpp - "race/race_manager.hpp" - - - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "config/stk_config.hpp" - "graphics/irr_driver.hpp" - "input/device_manager.hpp" - "input/input_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties_manager.hpp" - "modes/cutscene_world.hpp" - "modes/demo_world.hpp" - "modes/follow_the_leader.hpp" - "modes/overworld.hpp" - "modes/profile_world.hpp" - "modes/standard_race.hpp" - "modes/world.hpp" - "modes/three_strikes_battle.hpp" - "network/network_manager.hpp" - "states_screens/grand_prix_lose.hpp" - "states_screens/grand_prix_win.hpp" - "states_screens/kart_selection.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/state_manager.hpp" - "tracks/track_manager.hpp" - "utils/ptr_vector.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\grand_prix_lose.hpp - "guiengine/screen.hpp" - "karts/kart_model.hpp" - - - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\grand_prix_win.hpp - "guiengine/screen.hpp" - "karts/kart_model.hpp" - -1330468430 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\replay\replay_base.cpp - "replay/replay_base.hpp" - "io/file_manager.hpp" - "race/race_manager.hpp" - -1331774496 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\replay\replay_play.cpp - "replay/replay_play.hpp" - "config/stk_config.hpp" - "io/file_manager.hpp" - "karts/ghost_kart.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - - - -1352023716 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\replay\replay_recorder.cpp - "replay/replay_recorder.hpp" - "config/stk_config.hpp" - "io/file_manager.hpp" - "karts/ghost_kart.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - - - - -1350157788 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\addons_screen.cpp - "states_screens/addons_screen.hpp" - - "addons/addons_manager.hpp" - "addons/inetwork_http.hpp" - "guiengine/CGUISpriteBank.h" - "guiengine/modaldialog.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "io/file_manager.hpp" - "states_screens/dialogs/addons_loading.hpp" - "states_screens/dialogs/message_dialog.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - "utils/ptr_vector.hpp" - -1332434990 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\arenas_screen.cpp - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "io/file_manager.hpp" - "states_screens/state_manager.hpp" - "states_screens/arenas_screen.hpp" - "states_screens/dialogs/track_info_dialog.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/random_generator.hpp" - "utils/translation.hpp" - - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\arenas_screen.hpp - "guiengine/screen.hpp" - -1329392722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\track_info_dialog.hpp - "guiengine/modaldialog.hpp" - "guiengine/widgets/check_box_widget.hpp" - -1347581018 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\credits.cpp - "states_screens/credits.hpp" - - "irrString.h" - "config/user_config.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "guiengine/widget.hpp" - "io/file_manager.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1353291572 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\cutscene_gui.cpp - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "graphics/irr_driver.hpp" - "states_screens/cutscene_gui.hpp" - -1325455956 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\add_device_dialog.cpp - "states_screens/dialogs/add_device_dialog.hpp" - "states_screens/dialogs/message_dialog.hpp" - "config/player.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/button_widget.hpp" - "input/device_manager.hpp" - "input/input_manager.hpp" - "states_screens/options_screen_players.hpp" - "states_screens/options_screen_input.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - "input/wiimote_manager.hpp" - - - -1283994122 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\add_device_dialog.hpp - "config/player.hpp" - "guiengine/modaldialog.hpp" - -1340296410 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_players.hpp - - - "guiengine/screen.hpp" - "states_screens/dialogs/enter_player_name_dialog.hpp" - -1306183878 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_input.hpp - - "guiengine/screen.hpp" - -1351983538 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\addons_loading.cpp - "states_screens/dialogs/addons_loading.hpp" - - "addons/addons_manager.hpp" - "addons/inetwork_http.hpp" - "addons/request.hpp" - "config/user_config.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets.hpp" - "input/input_manager.hpp" - "io/file_manager.hpp" - "states_screens/addons_screen.hpp" - "states_screens/dialogs/message_dialog.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1301267244 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\confirm_resolution_dialog.cpp - "states_screens/dialogs/confirm_resolution_dialog.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/label_widget.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1335368658 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\custom_video_settings.cpp - "states_screens/dialogs/custom_video_settings.hpp" - "config/user_config.hpp" - "guiengine/widgets/check_box_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "states_screens/options_screen_video.hpp" - "utils/translation.hpp" - - -1324588498 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\custom_video_settings.hpp - "guiengine/modaldialog.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_video.hpp - - "guiengine/screen.hpp" - -1341790518 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\enter_player_name_dialog.cpp - "states_screens/dialogs/enter_player_name_dialog.hpp" - - "audio/sfx_manager.hpp" - "challenges/unlock_manager.hpp" - "config/player.hpp" - "guiengine/engine.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "guiengine/widgets/text_box_widget.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - -1354018156 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\gp_info_dialog.cpp - "states_screens/dialogs/gp_info_dialog.hpp" - "audio/sfx_manager.hpp" - "challenges/unlock_manager.hpp" - "guiengine/engine.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "io/file_manager.hpp" - "race/grand_prix_manager.hpp" - "race/race_manager.hpp" - "states_screens/state_manager.hpp" - "states_screens/tracks_screen.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/translation.hpp" - - - - -1283994122 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\gp_info_dialog.hpp - "guiengine/modaldialog.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\tracks_screen.hpp - "guiengine/screen.hpp" - - -1335397922 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\message_dialog.cpp - "states_screens/dialogs/message_dialog.hpp" - "guiengine/engine.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - -1341790518 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\player_info_dialog.cpp - "states_screens/dialogs/player_info_dialog.hpp" - - - "audio/sfx_manager.hpp" - "challenges/unlock_manager.hpp" - "config/player.hpp" - "guiengine/engine.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets/text_box_widget.hpp" - "states_screens/options_screen_players.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1303592218 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\player_info_dialog.hpp - "config/player.hpp" - "guiengine/modaldialog.hpp" - -1290651382 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\press_a_key_dialog.cpp - "guiengine/engine.hpp" - "input/input.hpp" - "input/input_manager.hpp" - "states_screens/dialogs/press_a_key_dialog.hpp" - "states_screens/options_screen_input2.hpp" - "utils/translation.hpp" - -1283994122 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\press_a_key_dialog.hpp - "guiengine/modaldialog.hpp" - -1353976898 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\race_over_dialog.cpp - "states_screens/dialogs/race_over_dialog.hpp" - "challenges/unlock_manager.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets.hpp" - "io/file_manager.hpp" - "input/input_manager.hpp" - "karts/abstract_kart.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/overworld.hpp" - "modes/three_strikes_battle.hpp" - "modes/world.hpp" - "modes/world_with_rank.hpp" - "network/network_manager.hpp" - "race/race_manager.hpp" - "states_screens/feature_unlocked.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/race_setup_screen.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - -1353976898 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\feature_unlocked.hpp - "graphics/irr_driver.hpp" - "guiengine/screen.hpp" - "race/race_manager.hpp" - "utils/ptr_vector.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_setup_screen.hpp - "guiengine/screen.hpp" - -1354839960 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\race_paused_dialog.cpp - "states_screens/dialogs/race_paused_dialog.hpp" - - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "input/input_manager.hpp" - "io/file_manager.hpp" - "modes/overworld.hpp" - "modes/world.hpp" - "network/network_manager.hpp" - "race/race_manager.hpp" - "states_screens/help_screen_1.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/race_setup_screen.hpp" - "states_screens/options_screen_video.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_1.hpp - "guiengine/screen.hpp" - -1352598112 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\select_challenge.cpp - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "guiengine/engine.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "input/device_manager.hpp" - "input/input_manager.hpp" - "io/file_manager.hpp" - "modes/world.hpp" - "network/network_manager.hpp" - "race/grand_prix_manager.hpp" - "race/race_manager.hpp" - "states_screens/dialogs/select_challenge.hpp" - "tracks/track_manager.hpp" - "tracks/track.hpp" - -1323122442 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\story_mode_new.cpp - "states_screens/dialogs/story_mode_new.hpp" - "config/user_config.hpp" - "config/player.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - - -1323040014 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\story_mode_new.hpp - "guiengine/modaldialog.hpp" - -1352238810 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\dialogs\track_info_dialog.cpp - "states_screens/dialogs/track_info_dialog.hpp" - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "io/file_manager.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "network/network_manager.hpp" - "race/highscores.hpp" - "race/highscore_manager.hpp" - "race/race_manager.hpp" - "states_screens/state_manager.hpp" - "states_screens/tracks_screen.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - - -1354331448 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\feature_unlocked.cpp - "states_screens/feature_unlocked.hpp" - - "audio/music_manager.hpp" - "challenges/challenge_data.hpp" - "challenges/game_slot.hpp" - "challenges/unlock_manager.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "io/file_manager.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/overworld.hpp" - "modes/world.hpp" - "race/grand_prix_manager.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/state_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/translation.hpp" - - - - - - - -1353976898 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\grand_prix_lose.cpp - "states_screens/grand_prix_lose.hpp" - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets/label_widget.hpp" - "io/file_manager.hpp" - "items/item_manager.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/overworld.hpp" - "states_screens/feature_unlocked.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - - - - - - -1353976898 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\grand_prix_win.cpp - "states_screens/grand_prix_win.hpp" - - - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets/label_widget.hpp" - "io/file_manager.hpp" - "items/item_manager.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "states_screens/feature_unlocked.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - - - - - - - -1343337856 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_1.cpp - "states_screens/help_screen_1.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "states_screens/help_screen_2.hpp" - "states_screens/help_screen_3.hpp" - "states_screens/help_screen_4.hpp" - "states_screens/state_manager.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_2.hpp - "guiengine/screen.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_3.hpp - "guiengine/screen.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_4.hpp - "guiengine/screen.hpp" - -1343337856 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_2.cpp - "states_screens/help_screen_2.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "states_screens/help_screen_1.hpp" - "states_screens/help_screen_3.hpp" - "states_screens/help_screen_4.hpp" - "states_screens/state_manager.hpp" - -1343337856 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_3.cpp - "states_screens/help_screen_3.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "modes/world.hpp" - "states_screens/help_screen_1.hpp" - "states_screens/help_screen_2.hpp" - "states_screens/help_screen_4.hpp" - "states_screens/state_manager.hpp" - -1343337856 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\help_screen_4.cpp - "states_screens/help_screen_4.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "modes/world.hpp" - "states_screens/help_screen_1.hpp" - "states_screens/help_screen_2.hpp" - "states_screens/help_screen_3.hpp" - "states_screens/state_manager.hpp" - -1354838506 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\kart_selection.cpp - "challenges/unlock_manager.hpp" - "config/player.hpp" - "config/user_config.hpp" - "kart_selection.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/bubble_widget.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "guiengine/widgets/model_view_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "input/input.hpp" - "input/input_manager.hpp" - "input/device_manager.hpp" - "input/input_device.hpp" - "items/item_manager.hpp" - "io/file_manager.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/overworld.hpp" - "states_screens/race_setup_screen.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - "utils/random_generator.hpp" - "utils/string_utils.hpp" - - - - - - -1350860794 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\main_menu_screen.cpp - "states_screens/main_menu_screen.hpp" - - "addons/inetwork_http.hpp" - "challenges/game_slot.hpp" - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widgets/label_widget.hpp" - "guiengine/widgets/list_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "input/device_manager.hpp" - "input/input_manager.hpp" - "io/file_manager.hpp" - "karts/kart_properties_manager.hpp" - "main_loop.hpp" - "modes/cutscene_world.hpp" - "modes/overworld.hpp" - "modes/demo_world.hpp" - "network/network_manager.hpp" - "states_screens/addons_screen.hpp" - "states_screens/credits.hpp" - "states_screens/help_screen_1.hpp" - "states_screens/kart_selection.hpp" - "states_screens/options_screen_video.hpp" - "states_screens/state_manager.hpp" - "states_screens/tutorial_screen.hpp" - "states_screens/feature_unlocked.hpp" - "states_screens/grand_prix_lose.hpp" - "states_screens/grand_prix_win.hpp" - "states_screens/dialogs/message_dialog.hpp" - "addons/news_manager.hpp" - "tracks/track_manager.hpp" - "tracks/track.hpp" - "utils/string_utils.hpp" - -1306089086 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\tutorial_screen.hpp - - "guiengine/screen.hpp" - "guiengine/CGUISpriteBank.h" - -1351466360 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\minimal_race_gui.cpp - "states_screens/minimal_race_gui.hpp" - "config/user_config.hpp" - "graphics/camera.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/scalable_font.hpp" - "io/file_manager.hpp" - "input/input.hpp" - "input/input_manager.hpp" - "items/attachment.hpp" - "items/attachment_manager.hpp" - "items/powerup_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/follow_the_leader.hpp" - "modes/linear_world.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1330042748 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_audio.cpp - "states_screens/options_screen_audio.hpp" - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "audio/sfx_base.hpp" - "graphics/irr_driver.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/check_box_widget.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "guiengine/widget.hpp" - "io/file_manager.hpp" - "states_screens/options_screen_input.hpp" - "states_screens/options_screen_players.hpp" - "states_screens/options_screen_ui.hpp" - "states_screens/options_screen_video.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - - - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_audio.hpp - - "guiengine/screen.hpp" - -1336953866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_ui.hpp - - "guiengine/screen.hpp" - -1354838644 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_input.cpp - "states_screens/options_screen_input.hpp" - "graphics/irr_driver.hpp" - "guiengine/CGUISpriteBank.h" - "guiengine/screen.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/list_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "input/input_manager.hpp" - "input/device_manager.hpp" - "io/file_manager.hpp" - "states_screens/options_screen_input2.hpp" - "states_screens/options_screen_audio.hpp" - "states_screens/options_screen_players.hpp" - "states_screens/options_screen_video.hpp" - "states_screens/options_screen_ui.hpp" - "states_screens/dialogs/add_device_dialog.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - - -1350090166 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_input2.cpp - "states_screens/options_screen_input2.hpp" - "graphics/irr_driver.hpp" - "guiengine/CGUISpriteBank.h" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/label_widget.hpp" - "guiengine/widgets/list_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "input/input_manager.hpp" - "input/device_manager.hpp" - "io/file_manager.hpp" - "states_screens/dialogs/press_a_key_dialog.hpp" - "states_screens/options_screen_audio.hpp" - "states_screens/options_screen_input.hpp" - "states_screens/options_screen_players.hpp" - "states_screens/options_screen_video.hpp" - "states_screens/options_screen_ui.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - - - - -1353028222 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_players.cpp - "states_screens/options_screen_players.hpp" - "challenges/unlock_manager.hpp" - "config/player.hpp" - "config/device_config.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/list_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "states_screens/dialogs/enter_player_name_dialog.hpp" - "states_screens/dialogs/player_info_dialog.hpp" - "states_screens/options_screen_audio.hpp" - "states_screens/options_screen_input.hpp" - "states_screens/options_screen_video.hpp" - "states_screens/options_screen_ui.hpp" - "states_screens/state_manager.hpp" - "states_screens/story_mode_lobby.hpp" - - - - -1348120280 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_ui.cpp - "states_screens/options_screen_ui.hpp" - "addons/inetwork_http.hpp" - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "audio/sfx_base.hpp" - "graphics/irr_driver.hpp" - "guiengine/scalable_font.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/check_box_widget.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/list_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "guiengine/widget.hpp" - "io/file_manager.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/options_screen_audio.hpp" - "states_screens/options_screen_input.hpp" - "states_screens/options_screen_players.hpp" - "states_screens/options_screen_video.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - -1335368658 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\options_screen_video.cpp - "states_screens/options_screen_video.hpp" - "audio/music_manager.hpp" - "audio/sfx_manager.hpp" - "audio/sfx_base.hpp" - "graphics/irr_driver.hpp" - "guiengine/screen.hpp" - "guiengine/widgets/button_widget.hpp" - "guiengine/widgets/check_box_widget.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "guiengine/widget.hpp" - "io/file_manager.hpp" - "states_screens/dialogs/custom_video_settings.hpp" - "states_screens/options_screen_audio.hpp" - "states_screens/options_screen_input.hpp" - "states_screens/options_screen_players.hpp" - "states_screens/options_screen_ui.hpp" - "states_screens/state_manager.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - -1351466360 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_gui.cpp - "states_screens/race_gui.hpp" - - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "graphics/camera.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/scalable_font.hpp" - "io/file_manager.hpp" - "input/input.hpp" - "input/input_manager.hpp" - "items/attachment.hpp" - "items/attachment_manager.hpp" - "items/powerup_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/follow_the_leader.hpp" - "modes/linear_world.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - -1354364086 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_gui_base.cpp - "states_screens/race_gui_base.hpp" - - - - - "audio/music_manager.hpp" - "graphics/irr_driver.hpp" - "graphics/material.hpp" - "graphics/material_manager.hpp" - "graphics/referee.hpp" - "guiengine/scalable_font.hpp" - "io/file_manager.hpp" - "items/attachment_manager.hpp" - "items/powerup.hpp" - "karts/abstract_kart.hpp" - "karts/abstract_kart_animation.hpp" - "karts/controller/controller.hpp" - "karts/explosion_animation.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "karts/rescue_animation.hpp" - "modes/follow_the_leader.hpp" - "modes/world.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - - -1353844224 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_gui_overworld.cpp - "states_screens/race_gui_overworld.hpp" - - "challenges/unlock_manager.hpp" - "config/user_config.hpp" - "graphics/camera.hpp" - "graphics/irr_driver.hpp" - "graphics/material_manager.hpp" - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/scalable_font.hpp" - "io/file_manager.hpp" - "input/input.hpp" - "input/input_manager.hpp" - "items/attachment.hpp" - "items/attachment_manager.hpp" - "items/powerup_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/world.hpp" - "race/grand_prix_manager.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - -1354839960 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_result_gui.cpp - "states_screens/race_result_gui.hpp" - "audio/music_manager.hpp" - "audio/sfx_base.hpp" - "challenges/unlock_manager.hpp" - "graphics/material.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "io/file_manager.hpp" - "karts/abstract_kart.hpp" - "karts/controller/controller.hpp" - "karts/kart_properties.hpp" - "karts/kart_properties_manager.hpp" - "modes/cutscene_world.hpp" - "modes/demo_world.hpp" - "modes/overworld.hpp" - "modes/world_with_rank.hpp" - "race/highscores.hpp" - "states_screens/dialogs/race_over_dialog.hpp" - "states_screens/feature_unlocked.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/race_setup_screen.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/string_utils.hpp" - -1350860794 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\race_setup_screen.cpp - "challenges/unlock_manager.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/ribbon_widget.hpp" - "guiengine/widgets/spinner_widget.hpp" - "io/file_manager.hpp" - "race/race_manager.hpp" - "states_screens/arenas_screen.hpp" - "states_screens/state_manager.hpp" - "states_screens/tracks_screen.hpp" - "utils/translation.hpp" - "states_screens/race_setup_screen.hpp" - -1351465596 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\state_manager.cpp - "states_screens/state_manager.hpp" - "audio/sfx_manager.hpp" - "audio/music_manager.hpp" - "config/stk_config.hpp" - "graphics/irr_driver.hpp" - "guiengine/engine.hpp" - "guiengine/modaldialog.hpp" - "guiengine/screen.hpp" - "input/input_device.hpp" - "input/input_manager.hpp" - "main_loop.hpp" - "modes/profile_world.hpp" - "modes/world.hpp" - "utils/translation.hpp" - -1351466398 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\story_mode_lobby.cpp - "states_screens/story_mode_lobby.hpp" - "challenges/unlock_manager.hpp" - "guiengine/widgets/check_box_widget.hpp" - "guiengine/widgets/list_widget.hpp" - "states_screens/dialogs/enter_player_name_dialog.hpp" - "states_screens/main_menu_screen.hpp" - "states_screens/state_manager.hpp" - -1354018156 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\tracks_screen.cpp - "challenges/unlock_manager.hpp" - "graphics/irr_driver.hpp" - "guiengine/widget.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "guiengine/widgets/icon_button_widget.hpp" - "io/file_manager.hpp" - "race/grand_prix_data.hpp" - "race/grand_prix_manager.hpp" - "states_screens/state_manager.hpp" - "states_screens/tracks_screen.hpp" - "states_screens/dialogs/gp_info_dialog.hpp" - "states_screens/dialogs/track_info_dialog.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/translation.hpp" - - -1350860794 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\states_screens\tutorial_screen.cpp - "states_screens/tutorial_screen.hpp" - "guiengine/widgets/list_widget.hpp" - "utils/string_utils.hpp" - "graphics/irr_driver.hpp" - "tutorial/tutorial_manager.hpp" - "config/user_config.hpp" - "guiengine/engine.hpp" - "guiengine/widgets/dynamic_ribbon_widget.hpp" - "input/device_manager.hpp" - "input/input_manager.hpp" - "io/file_manager.hpp" - "karts/kart_properties_manager.hpp" - "network/network_manager.hpp" - "race/race_manager.hpp" - "states_screens/state_manager.hpp" - "utils/translation.hpp" - - "irrString.h" - -1300401266 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\dictionary.cpp - - "log_stream.hpp" - "dictionary.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\log_stream.hpp - "log.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\log.hpp - - -1306089712 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\dictionary_manager.cpp - "dictionary_manager.hpp" - - - - - - - "log_stream.hpp" - "po_parser.hpp" - "stk_file_system.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\po_parser.hpp - - - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\stk_file_system.hpp - "file_system.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\file_system.hpp - - - - - -1300320148 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\iconv.cpp - - - - - - - - "iconv.hpp" - "log_stream.hpp" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\iconv.hpp - - "SDL.h" - - -1355091350 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\language.cpp - "language.hpp" - - - - -1300320148 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\log.cpp - - "log.hpp" - -1300320148 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\plural_forms.cpp - "plural_forms.hpp" - - -1300320148 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\po_parser.cpp - "po_parser.hpp" - - - - - - - - "language.hpp" - "log_stream.hpp" - "iconv.hpp" - "dictionary.hpp" - "plural_forms.hpp" - -1300320148 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\stk_file_system.cpp - "stk_file_system.hpp" - - - - - "io/file_manager.hpp" - -1300320148 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tinygettext\tinygettext.cpp - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\ambient_light_sphere.cpp - "tracks/ambient_light_sphere.hpp" - - - "graphics/camera.hpp" - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - -1329392722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\ambient_light_sphere.hpp - - "tracks/check_sphere.hpp" - -1329392722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_sphere.hpp - "tracks/check_structure.hpp" - -1333417614 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_structure.hpp - - "utils/aligned_array.hpp" - "utils/vec3.hpp" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\bezier_curve.cpp - "tracks/bezier_curve.hpp" - "io/xml_node.hpp" - -1337034402 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_cannon.cpp - "tracks/check_cannon.hpp" - "animations/animation_base.hpp" - "animations/ipo.hpp" - "graphics/show_curve.hpp" - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "karts/cannon_animation.hpp" - "modes/world.hpp" - -1337034402 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_cannon.hpp - "animations/animation_base.hpp" - "tracks/check_line.hpp" - -1334786866 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_line.hpp - - - - "tracks/check_structure.hpp" - -1332188472 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_lap.cpp - "tracks/check_lap.hpp" - - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "modes/linear_world.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - -1329392722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_lap.hpp - "tracks/check_structure.hpp" - -1336460836 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_line.cpp - "tracks/check_line.hpp" - "graphics/irr_driver.hpp" - "io/xml_node.hpp" - "karts/abstract_kart.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "irrlicht.h" - - - -1333417614 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_manager.cpp - "tracks/check_manager.hpp" - - - "io/xml_node.hpp" - "tracks/ambient_light_sphere.hpp" - "tracks/check_cannon.hpp" - "tracks/check_lap.hpp" - "tracks/check_line.hpp" - "tracks/check_structure.hpp" - "tracks/track.hpp" - -1329392722 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_manager.hpp - "utils/no_copy.hpp" - - - - -1329392722 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_sphere.cpp - "tracks/check_sphere.hpp" - - - "io/xml_node.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - -1334754858 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\check_structure.cpp - "tracks/check_structure.hpp" - - "karts/abstract_kart.hpp" - "modes/linear_world.hpp" - "modes/world.hpp" - "race/race_manager.hpp" - "tracks/check_lap.hpp" - "tracks/check_manager.hpp" - -1346712570 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\graph_node.cpp - "tracks/quad_graph.hpp" - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "tracks/quad_graph.hpp" - "tracks/quad_set.hpp" - -1336266784 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\lod_node_loader.cpp - "tracks/lod_node_loader.hpp" - "graphics/irr_driver.hpp" - "graphics/lod_node.hpp" - "io/xml_node.hpp" - "tracks/track.hpp" - - - - - -1336266784 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\lod_node_loader.hpp - - - - "io/xml_node.hpp" - -1350852514 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\quad.cpp - "tracks/quad.hpp" - - - - "LinearMath/btTransform.h" - -1350878512 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\quad_graph.cpp - "tracks/quad_graph.hpp" - "LinearMath/btTransform.h" - - - "config/user_config.hpp" - "graphics/irr_driver.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "modes/world.hpp" - "tracks/check_lap.hpp" - "tracks/check_line.hpp" - "tracks/check_manager.hpp" - "tracks/quad_set.hpp" - "tracks/track.hpp" - -1344409076 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\quad_set.cpp - "tracks/quad_set.hpp" - - - "io/file_manager.hpp" - "io/xml_node.hpp" - "utils/string_utils.hpp" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\terrain_info.cpp - "tracks/terrain_info.hpp" - - "modes/world.hpp" - "physics/triangle_mesh.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "utils/constants.hpp" - -1353374884 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track.cpp - "tracks/track.hpp" - - - - - "audio/music_manager.hpp" - "challenges/challenge.hpp" - "challenges/unlock_manager.hpp" - "config/stk_config.hpp" - "config/user_config.hpp" - "graphics/camera.hpp" - "graphics/CBatchingMesh.hpp" - "graphics/irr_driver.hpp" - "graphics/lod_node.hpp" - "graphics/material_manager.hpp" - "graphics/mesh_tools.hpp" - "graphics/moving_texture.hpp" - "graphics/particle_emitter.hpp" - "graphics/particle_kind.hpp" - "graphics/particle_kind_manager.hpp" - "guiengine/scalable_font.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "items/item.hpp" - "items/item_manager.hpp" - "modes/linear_world.hpp" - "modes/world.hpp" - "physics/physical_object.hpp" - "physics/physics.hpp" - "physics/triangle_mesh.hpp" - "race/race_manager.hpp" - "tracks/bezier_curve.hpp" - "tracks/check_manager.hpp" - "tracks/lod_node_loader.hpp" - "tracks/track_manager.hpp" - "tracks/quad_graph.hpp" - "tracks/quad_set.hpp" - "tracks/track_object_manager.hpp" - "utils/constants.hpp" - "utils/string_utils.hpp" - "utils/translation.hpp" - - - - - - -1335298712 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_manager.cpp - "tracks/track_manager.hpp" - - - - - - "audio/music_manager.hpp" - "config/stk_config.hpp" - "io/file_manager.hpp" - "tracks/track.hpp" - -1349007362 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_object.cpp - "tracks/track_object.hpp" - "audio/sfx_base.hpp" - "audio/sfx_buffer.hpp" - "audio/sfx_manager.hpp" - "graphics/irr_driver.hpp" - "graphics/lod_node.hpp" - "graphics/particle_emitter.hpp" - "graphics/particle_kind_manager.hpp" - "io/file_manager.hpp" - "io/xml_node.hpp" - "items/item_manager.hpp" - "modes/overworld.hpp" - "modes/world.hpp" - "states_screens/dialogs/race_paused_dialog.hpp" - "tracks/track.hpp" - - - - -1352033824 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_object_manager.cpp - "tracks/track_object_manager.hpp" - "animations/ipo.hpp" - "config/user_config.hpp" - "animations/billboard_animation.hpp" - "animations/three_d_animation.hpp" - "graphics/irr_driver.hpp" - "graphics/lod_node.hpp" - "graphics/material_manager.hpp" - "io/xml_node.hpp" - "physics/physical_object.hpp" - "tracks/track_object.hpp" - - - -1341989312 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tracks\track_sector.cpp - "modes/linear_world.hpp" - "modes/world.hpp" - "tracks/check_manager.hpp" - "tracks/check_structure.hpp" - "tracks/track.hpp" - "tracks/track_sector.hpp" - "tracks/quad_graph.hpp" - -1292540234 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tutorial\tutorial.cpp - "tutorial.hpp" - -1350860794 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tutorial\tutorial_data.cpp - "tutorial/tutorial_data.hpp" - - - "karts/abstract_kart.hpp" - "karts/kart_properties_manager.hpp" - "modes/linear_world.hpp" - "race/grand_prix_data.hpp" - "race/grand_prix_manager.hpp" - "race/race_manager.hpp" - "tracks/track.hpp" - "tracks/track_manager.hpp" - "utils/translation.hpp" - -1306698744 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\tutorial\tutorial_manager.cpp - "tutorial/tutorial_manager.hpp" - - - - - - "audio/sfx_base.hpp" - "audio/sfx_manager.hpp" - "config/user_config.hpp" - "io/file_manager.hpp" - "karts/kart_properties_manager.hpp" - "race/race_manager.hpp" - "tracks/track_manager.hpp" - "utils/string_utils.hpp" - -1355181700 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\constants.cpp - -1344304468 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\leak_check.cpp - "utils/leak_check.hpp" - "utils/synchronised.hpp" - "utils/ptr_vector.hpp" - - - - - - -1311305198 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\profiler.cpp - "profiler.hpp" - "graphics/irr_driver.hpp" - "guiengine/event_handler.hpp" - "guiengine/engine.hpp" - "guiengine/scalable_font.hpp" - - - - - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\random_generator.cpp - "utils/random_generator.hpp" - - - -1353676418 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\string_utils.cpp - "utils/string_utils.hpp" - "coreutil.h" - "math.h" - - - - - - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\translation.cpp - "utils/translation.hpp" - - - - - - - - - - "io/file_manager.hpp" - "utils/constants.hpp" - "utils/utf8.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi.h - "fribidi-common.h" - "fribidi-unicode.h" - "fribidi-types.h" - "fribidi-flags.h" - "fribidi-bidi-types.h" - "fribidi-bidi.h" - "fribidi-joining-types.h" - "fribidi-joining.h" - "fribidi-mirroring.h" - "fribidi-arabic.h" - "fribidi-shape.h" - "fribidi-char-sets.h" - "fribidi-deprecated.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1277982602 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-common.h - "fribidi-config.h" - - - -1277980596 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-config.h - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-unicode.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-begindecls.h" - "fribidi-unicode-version.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-types.h - "fribidi-common.h" - "fribidi-begindecls.h" - - - - - - - - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-begindecls.h - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-enddecls.h - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-unicode-version.h - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-flags.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-bidi-types.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-begindecls.h" - "fribidi-bidi-types-list.h" - "fribidi-bidi-types-list.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-bidi-types-list.h - "fribidi-bidi-types-list.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-bidi.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-flags.h" - "fribidi-bidi-types.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-joining-types.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-begindecls.h" - "fribidi-joining-types-list.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-joining-types-list.h - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-joining.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-bidi-types.h" - "fribidi-joining-types.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-mirroring.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-bidi-types.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-arabic.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-flags.h" - "fribidi-bidi-types.h" - "fribidi-joining.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-shape.h - "fribidi-types.h" - "fribidi-flags.h" - "fribidi-bidi-types.h" - "fribidi-joining-types.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1277980616 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-char-sets.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-begindecls.h" - "fribidi-char-sets-list.h" - "fribidi-enddecls.h" - -1277980616 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-char-sets-list.h - -1277980558 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\dependencies\fribidi\fribidi-deprecated.h - "fribidi-common.h" - "fribidi-types.h" - "fribidi-bidi-types.h" - "fribidi-begindecls.h" - "fribidi-enddecls.h" - -1300320148 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\utf8.h - "utf8/checked.h" - "utf8/unchecked.h" - -1336460992 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\utf8\checked.h - "core.h" - - -1306963158 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\utf8\core.h - - -1306963158 d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\utf8\unchecked.h - "core.h" - -1323197016 source:d:\artifacts\projects\gsoc\gsoc2013\super tuxkart\trail2\supertuxkart-0.8\src\utils\vec3.cpp - "utils/vec3.hpp" - diff --git a/src/ide/codeblocks/supertuxkart.layout b/src/ide/codeblocks/supertuxkart.layout deleted file mode 100644 index c0bc4258b..000000000 --- a/src/ide/codeblocks/supertuxkart.layout +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/ide/codeblocks/supertuxkart.workspace b/src/ide/codeblocks/supertuxkart.workspace deleted file mode 100644 index 24418fde1..000000000 --- a/src/ide/codeblocks/supertuxkart.workspace +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/ide/vc10/bullet_lib.vcxproj b/src/ide/vc10/bullet_lib.vcxproj deleted file mode 100644 index b366b3281..000000000 --- a/src/ide/vc10/bullet_lib.vcxproj +++ /dev/null @@ -1,448 +0,0 @@ - - - - - debug - Win32 - - - debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4} - bullet_lib - ManagedCProj - - - - StaticLibrary - MultiByte - false - - - StaticLibrary - MultiByte - false - - - StaticLibrary - MultiByte - false - true - - - StaticLibrary - MultiByte - false - true - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - - - - false - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - true - MultiThreaded - - - Level3 - ProgramDatabase - - - - - false - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - MultiThreaded - - - Level3 - ProgramDatabase - StreamingSIMDExtensions2 - - - - - Disabled - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - EditAndContinue - - - - - Disabled - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - ProgramDatabase - true - StreamingSIMDExtensions2 - - - - - true - true - - - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ide/vc10/enet.vcxproj b/src/ide/vc10/enet.vcxproj deleted file mode 100644 index b539ee67c..000000000 --- a/src/ide/vc10/enet.vcxproj +++ /dev/null @@ -1,160 +0,0 @@ - - - - - debug - Win32 - - - debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF} - enet - Win32Proj - - - - StaticLibrary - Unicode - true - - - StaticLibrary - Unicode - true - - - StaticLibrary - Unicode - true - - - StaticLibrary - Unicode - true - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ide/vc10/supertuxkart.sln b/src/ide/vc10/supertuxkart.sln deleted file mode 100644 index 6ae4a4b0a..000000000 --- a/src/ide/vc10/supertuxkart.sln +++ /dev/null @@ -1,74 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "supertuxkart", "supertuxkart.vcxproj", "{B1BC2764-1A43-4800-A654-788B0D05EDA2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bullet_lib", "bullet_lib.vcxproj", "{54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enet", "enet.vcxproj", "{B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Irrlicht", "..\..\..\lib\irrlicht\source\Irrlicht\Irrlicht10.0.vcxproj", "{E08E042A-6C45-411B-92BE-3CC31331019F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - debug|experimental_x64 = debug|experimental_x64 - debug|Win32 = debug|Win32 - Release - Fast FPU|experimental_x64 = Release - Fast FPU|experimental_x64 - Release - Fast FPU|Win32 = Release - Fast FPU|Win32 - Release|experimental_x64 = Release|experimental_x64 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|experimental_x64.ActiveCfg = debug|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|experimental_x64.Build.0 = debug|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|Win32.ActiveCfg = debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|Win32.Build.0 = debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|experimental_x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|experimental_x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|experimental_x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|experimental_x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|experimental_x64.ActiveCfg = debug|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|experimental_x64.Build.0 = debug|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|Win32.ActiveCfg = debug|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|Win32.Build.0 = debug|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|experimental_x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|experimental_x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|experimental_x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|experimental_x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|experimental_x64.ActiveCfg = debug|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|experimental_x64.Build.0 = debug|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|Win32.ActiveCfg = debug|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|Win32.Build.0 = debug|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|experimental_x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|experimental_x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|experimental_x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|experimental_x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.Build.0 = Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|experimental_x64.ActiveCfg = Static lib - Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|experimental_x64.Build.0 = Static lib - Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|Win32.ActiveCfg = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|Win32.Build.0 = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|experimental_x64.ActiveCfg = Static lib - Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|experimental_x64.Build.0 = Static lib - Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|Win32.ActiveCfg = Static lib - Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|Win32.Build.0 = Static lib - Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|experimental_x64.ActiveCfg = Static lib - Release|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|experimental_x64.Build.0 = Static lib - Release|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|Win32.ActiveCfg = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|Win32.Build.0 = Static lib - Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/ide/vc10/supertuxkart.vcxproj b/src/ide/vc10/supertuxkart.vcxproj deleted file mode 100644 index e3cdeabda..000000000 --- a/src/ide/vc10/supertuxkart.vcxproj +++ /dev/null @@ -1,753 +0,0 @@ - - - - - debug - Win32 - - - debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {B1BC2764-1A43-4800-A654-788B0D05EDA2} - supertuxkart - Win32Proj - - - - Application - MultiByte - - - Application - NotSet - v100 - - - Application - MultiByte - true - - - Application - MultiByte - true - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - ..\..\../ - ..\..\../ - $(Configuration)\ - $(Configuration)\ - false - false - ..\..\../ - ..\..\../ - $(Configuration)\ - $(Configuration)\ - true - true - $(ProjectName)_d - $(ProjectName)_d - C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include;$(IncludePath) - - - - true - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - HAVE_OGGVORBIS;NDEBUG;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;BT_NO_PROFILE;PACKAGE="supertuxkart";ENABLE_BIDI;ADDONS_MANAGER;_IRR_STATIC_LIB_;%(PreprocessorDefinitions) - MultiThreadedDLL - - - Level3 - ProgramDatabase - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;fribidi.lib;pthreadVC2.lib;libcurl_imp.lib;%(AdditionalDependencies) - $(OutDir)/$(ProjectName).exe - ../../../dependencies/lib;../../../lib/irrlicht/lib/Win32-visualstudio;%(AdditionalLibraryDirectories) - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - Windows - true - true - false - - - MachineX86 - - - - - true - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - HAVE_OGGVORBIS;NDEBUG;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;BT_NO_PROFILE;PACKAGE="supertuxkart";ENABLE_BIDI;WIN64;_IRR_STATIC_LIB_;%(PreprocessorDefinitions) - MultiThreadedDLL - NotUsing - Level3 - ProgramDatabase - StreamingSIMDExtensions2 - true - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;libfribidi.lib;pthreadVC2.lib;libcurl.lib;%(AdditionalDependencies) - $(OutDir)/$(ProjectName).exe - ../../../lib/irrlicht/lib/Win64-visualStudio;../../../dependencies/lib;%(AdditionalLibraryDirectories) - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - Windows - true - true - false - - - - - - - Disabled - false - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - BT_NO_PROFILE;HAVE_OGGVORBIS;_DEBUG;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;DEBUG;PACKAGE="supertuxkart";_CRTDBG_MAP_ALLOC;ENABLE_BIDI;_IRR_STATIC_LIB_;%(PreprocessorDefinitions) - false - false - true - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - EditAndContinue - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;fribidi.lib;pthreadVC2.lib;libcurl_imp.lib;%(AdditionalDependencies) - $(TargetPath) - ../../../dependencies/lib;../../../lib/irrlicht/lib/Win32-visualstudio;%(AdditionalLibraryDirectories) - false - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - false - - - false - Windows - - - false - - - MachineX86 - false - - - - - Disabled - false - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - BT_NO_PROFILE;HAVE_OGGVORBIS;_DEBUG;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;DEBUG;PACKAGE="supertuxkart";_CRTDBG_MAP_ALLOC;ENABLE_BIDI;ADDONS_MANAGER;_WIN64;_IRR_STATIC_LIB_;%(PreprocessorDefinitions) - false - false - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - ProgramDatabase - false - StreamingSIMDExtensions2 - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;libfribidi.lib;pthreadVC2.lib;libcurl.lib;%(AdditionalDependencies) - $(TargetPath) - ../../../dependencies/lib;../../../lib/irrlicht/lib/Win64-VisualStudio;%(AdditionalLibraryDirectories) - false - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - false - - - false - Windows - - - false - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - false - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {e08e042a-6c45-411b-92be-3cc31331019f} - true - true - false - true - false - - - {54aa44b9-b07b-49da-8b1a-05bbfe8ad5d4} - false - - - {b0e92b97-089a-4d5b-bf17-77f1bc5daeef} - false - - - - - - \ No newline at end of file diff --git a/src/ide/vc10/supertuxkart.vcxproj.filters b/src/ide/vc10/supertuxkart.vcxproj.filters deleted file mode 100644 index 9d8e164d0..000000000 --- a/src/ide/vc10/supertuxkart.vcxproj.filters +++ /dev/null @@ -1,1701 +0,0 @@ - - - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {55621666-34fd-482a-b2a0-077db2794fc5} - - - {386e9299-f42e-4175-8f00-c205aa480876} - - - {865587a4-41b6-4832-a961-2f012204ef4c} - - - {1953a99d-3f65-4efe-93d3-b0adb25f970d} - - - {fa2fbc61-28e7-44cc-830d-7d31019b910c} - - - {b231fec4-a731-46b5-a8cf-1ef8fa0bf7be} - - - {06defe0c-00ba-4c91-b0f4-fe0890362f0f} - - - {9f93fb34-c97b-4108-9cd0-3f844d421e11} - - - {6c09fe56-1df1-4fdc-9023-47887316c0b2} - - - {1298df87-da0e-462e-b074-a496598571d8} - - - {2f186570-85e9-4cea-88dc-0f716c6e141a} - - - {f2f3feeb-648b-4edf-8025-fb86e1a6d9bb} - - - {1ea79cd5-39b0-4228-b002-7bddf4d45fda} - - - {24d03539-463f-4c91-9ea0-6fd4b5f1dde0} - - - {24740454-5060-4191-bc49-7ff560db2aba} - - - {03371ee6-3cf3-4ed8-b69e-21a2d0f70351} - - - {62844994-aa74-4a31-b255-1815884856e2} - - - {79bdc1db-0db0-4d69-9e25-c9404a5ab2ee} - - - {fbc2e0f1-a87f-4205-94b4-2a8805338d2c} - - - {533141f3-f1ce-4815-8e9c-4253fd5e9f5e} - - - {5c9f42c5-1a5b-476b-998f-f84384767684} - - - {8da5c73a-5884-4e8d-818a-20521a66e611} - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {88b6497f-6336-4d65-b0e3-5fad9ff35ffb} - - - {c4431a49-640d-48e5-9ac7-473955a15283} - - - {6eeebf67-9364-4c48-9e24-e89164897f9e} - - - {3ee2b9d8-e9b8-4af3-a5bf-28ae532ed5cc} - - - {1bda54ec-85a1-4725-8425-e5942887808d} - - - {0f42eff9-e52c-4975-bc4a-52b0310ddb99} - - - {c3be189e-ac78-42f5-b6e2-7193d9b32e80} - - - {b91bda77-4ae6-4d47-8de1-65b8b992251e} - - - {cd929227-595c-45e8-bf99-ab06e6d586d8} - - - {4bb34709-b7d7-4c3b-9f26-e7a26ffd817a} - - - {8d8a1efe-f3d5-4532-8271-101ed960defc} - - - {45a7521f-d7d3-4404-8f6f-62e2b08c8f4c} - - - {07a426a7-14fb-495b-bff3-5a402664e035} - - - {00e74e62-f481-4883-a49f-661a5b3c9d88} - - - {1e496486-52dd-4fe4-9bd0-3ec0f2b280fc} - - - {7d4fb5af-51c8-4073-bdbf-e0420a211096} - - - {7d4c8018-8cb8-4335-b0db-217883ee8954} - - - {29b448e9-cfd5-4024-a3d7-360ad6d89fed} - - - {305929ad-22c9-4dce-8514-2b3ceab47d2b} - - - {02e2a507-6184-43fb-aa71-46ef3fc77a37} - - - {9ea7a6dd-0714-414c-970c-881b61411ef8} - - - {376d4c71-33a2-4def-949e-31bcb5b72812} - - - {5faa19dc-78e6-438e-a2a0-6a489961f098} - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - {11a7d573-5959-4be8-8a3f-d455d7833333} - - - {d477b64b-eb9c-4059-a647-98f3a2f7203a} - - - - - Source Files - - - Source Files - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\challenges - - - Source Files\challenges - - - Source Files\challenges - - - Source Files\utils - - - Source Files\utils - - - Source Files\utils - - - Source Files\utils - - - Source Files\utils - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts\controller - - - Source Files\karts\controller - - - Source Files\karts\controller - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\io - - - Source Files\io - - - Source Files\io - - - Source Files\input - - - Source Files\input - - - Source Files\input - - - Source Files\input - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\config - - - Source Files\config - - - Source Files\config - - - Source Files\animations - - - Source Files\animations - - - Source Files\animations - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\addons - - - Source Files\addons - - - Source Files\addons - - - Source Files\addons - - - Source Files\graphics - - - Source Files\physics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\graphics - - - Source Files\states_screens - - - Source Files\guiengine\widgets - - - Source Files\addons - - - Source Files\addons - - - Source Files\items - - - Source Files\utils - - - Source Files\graphics - - - Source Files\items - - - Source Files\graphics - - - Source Files\karts - - - Source Files\tracks - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\utils - - - Source Files - - - Source Files\challenges - - - Source Files\states_screens - - - Source Files\states_screens\dialogs - - - Source Files\modes - - - Source Files\states_screens - - - Source Files\karts - - - Source Files\states_screens\dialogs - - - Source Files\replay - - - Source Files\replay - - - Source Files\replay - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\modes - - - Source Files\config - - - Source Files\tracks - - - Source Files\graphics - - - Source Files\karts\controller - - - Source Files\modes - - - Source Files\states_screens - - - Source Files\addons - - - Source Files\karts\controller - - - Source Files\states_screens - - - Source Files\tinygettext - - - Source Files\states_screens\dialogs - - - Source Files\utils - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\states_screens - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\input - - - Source Files - - - Source Files\guiengine\widgets - - - Source Files\utils - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\karts\controller - - - Source Files\karts\controller - - - Source Files\karts\controller - - - - - Header Files - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\challenges - - - Header Files\challenges - - - Header Files\challenges - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts\controller - - - Header Files\items\karts\controller - - - Header Files\items\karts\controller - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\io - - - Header Files\io - - - Header Files\io - - - Header Files\input - - - Header Files\input - - - Header Files\input - - - Header Files\input - - - Header Files\input - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\config - - - Header Files\config - - - Header Files\config - - - Header Files\config - - - Header Files\animations - - - Header Files\animations - - - Header Files\animations - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\addons - - - Header Files\addons - - - Header Files\addons - - - Header Files\addons - - - Header Files\graphics - - - Header Files\physics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\utils\utf8 - - - Header Files\utils\utf8 - - - Header Files\utils\utf8 - - - Header Files\utils - - - Header Files\graphics - - - Header Files\states_screens - - - Header Files\utils - - - Header Files\guiengine\widgets - - - Header Files\addons - - - Header Files\addons - - - Header Files\items - - - Header Files\utils - - - Header Files\graphics - - - Header Files\items - - - Header Files\graphics - - - Header Files\items\karts - - - Header Files\tracks - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\utils - - - Header Files - - - Header Files\challenges - - - Header Files\states_screens - - - Header Files\states_screens\dialogs - - - Header Files\modes - - - Header Files\states_screens - - - Header Files\items\karts - - - Header Files\states_screens\dialogs - - - Header Files\replay - - - Header Files\replay - - - Header Files\replay - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\items\karts - - - Header Files\modes - - - Header Files\tracks - - - Header Files\utils - - - Header Files\graphics - - - Header Files\items\karts\controller - - - Header Files\modes - - - Header Files\states_screens - - - Header Files\addons - - - Header Files\items\karts\controller - - - Header Files\utils - - - Header Files\tinygettext - - - Header Files\states_screens\dialogs - - - Header Files\utils - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\states_screens - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\input - - - Header Files - - - Header Files\guiengine\widgets - - - Header Files\tracks - - - Source Files\tracks - - - Header Files\tracks - - - Header Files\items\karts\controller - - - Header Files\items\karts\controller - - - \ No newline at end of file diff --git a/src/ide/vc11/bullet_lib.vcxproj b/src/ide/vc11/bullet_lib.vcxproj deleted file mode 100644 index 4bf6135bb..000000000 --- a/src/ide/vc11/bullet_lib.vcxproj +++ /dev/null @@ -1,452 +0,0 @@ - - - - - debug - Win32 - - - debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4} - bullet_lib - ManagedCProj - - - - StaticLibrary - MultiByte - false - v110 - - - StaticLibrary - MultiByte - false - v110 - - - StaticLibrary - MultiByte - false - true - v110 - - - StaticLibrary - MultiByte - false - true - v110 - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - - - - false - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - true - MultiThreadedDLL - - - Level3 - ProgramDatabase - - - - - false - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - MultiThreaded - - - Level3 - ProgramDatabase - StreamingSIMDExtensions2 - - - - - Disabled - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - EditAndContinue - - - - - Disabled - ..\..\..\lib\bullet\src;%(AdditionalIncludeDirectories) - WIN32;BT_NO_PROFILE;HAVE_GLUT;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - ProgramDatabase - true - StreamingSIMDExtensions2 - - - - - true - true - - - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ide/vc11/enet.vcxproj b/src/ide/vc11/enet.vcxproj deleted file mode 100644 index 82e4a71ec..000000000 --- a/src/ide/vc11/enet.vcxproj +++ /dev/null @@ -1,164 +0,0 @@ - - - - - debug - Win32 - - - debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF} - enet - Win32Proj - - - - StaticLibrary - Unicode - true - v110 - - - StaticLibrary - Unicode - true - v110 - - - StaticLibrary - Unicode - true - v110 - - - StaticLibrary - Unicode - true - v110 - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(Configuration)\ - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - true - - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - false - ../../../lib/enet/include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ide/vc11/supertuxkart.sln b/src/ide/vc11/supertuxkart.sln deleted file mode 100644 index 818e251a7..000000000 --- a/src/ide/vc11/supertuxkart.sln +++ /dev/null @@ -1,190 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Express 2012 for Windows Desktop -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "supertuxkart", "supertuxkart.vcxproj", "{B1BC2764-1A43-4800-A654-788B0D05EDA2}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bullet_lib", "bullet_lib.vcxproj", "{54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enet", "enet.vcxproj", "{B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Irrlicht", "..\..\..\lib\irrlicht\source\Irrlicht\Irrlicht11.0.vcxproj", "{E08E042A-6C45-411B-92BE-3CC31331019F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - debug|experimental_x64 = debug|experimental_x64 - debug|Win32 = debug|Win32 - debug|x64 = debug|x64 - Release - Fast FPU|experimental_x64 = Release - Fast FPU|experimental_x64 - Release - Fast FPU|Win32 = Release - Fast FPU|Win32 - Release - Fast FPU|x64 = Release - Fast FPU|x64 - Release|experimental_x64 = Release|experimental_x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - SDL-Debug|experimental_x64 = SDL-Debug|experimental_x64 - SDL-Debug|Win32 = SDL-Debug|Win32 - SDL-Debug|x64 = SDL-Debug|x64 - Static lib - Debug|experimental_x64 = Static lib - Debug|experimental_x64 - Static lib - Debug|Win32 = Static lib - Debug|Win32 - Static lib - Debug|x64 = Static lib - Debug|x64 - Static lib - Release - Fast FPU|experimental_x64 = Static lib - Release - Fast FPU|experimental_x64 - Static lib - Release - Fast FPU|Win32 = Static lib - Release - Fast FPU|Win32 - Static lib - Release - Fast FPU|x64 = Static lib - Release - Fast FPU|x64 - Static lib - Release|experimental_x64 = Static lib - Release|experimental_x64 - Static lib - Release|Win32 = Static lib - Release|Win32 - Static lib - Release|x64 = Static lib - Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|experimental_x64.ActiveCfg = debug|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|experimental_x64.Build.0 = debug|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|Win32.ActiveCfg = debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|Win32.Build.0 = debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|x64.ActiveCfg = debug|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|x64.Build.0 = debug|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|experimental_x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|experimental_x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release - Fast FPU|x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|experimental_x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|experimental_x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.SDL-Debug|experimental_x64.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.SDL-Debug|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.SDL-Debug|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.SDL-Debug|x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.SDL-Debug|x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Debug|experimental_x64.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Debug|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Debug|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Debug|x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Debug|x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release - Fast FPU|experimental_x64.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release - Fast FPU|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release - Fast FPU|x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release - Fast FPU|x64.Build.0 = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release|experimental_x64.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release|x64.ActiveCfg = Release|x64 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Static lib - Release|x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|experimental_x64.ActiveCfg = debug|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|experimental_x64.Build.0 = debug|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|Win32.ActiveCfg = debug|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|Win32.Build.0 = debug|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|x64.ActiveCfg = debug|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|x64.Build.0 = debug|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|experimental_x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|experimental_x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release - Fast FPU|x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|experimental_x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|experimental_x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.SDL-Debug|experimental_x64.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.SDL-Debug|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.SDL-Debug|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.SDL-Debug|x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.SDL-Debug|x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Debug|experimental_x64.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Debug|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Debug|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Debug|x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Debug|x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release - Fast FPU|experimental_x64.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release - Fast FPU|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release - Fast FPU|x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release - Fast FPU|x64.Build.0 = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release|experimental_x64.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release|x64.ActiveCfg = Release|x64 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Static lib - Release|x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|experimental_x64.ActiveCfg = debug|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|experimental_x64.Build.0 = debug|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|Win32.ActiveCfg = debug|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|Win32.Build.0 = debug|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|x64.ActiveCfg = debug|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|x64.Build.0 = debug|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|experimental_x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|experimental_x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release - Fast FPU|x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|experimental_x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|experimental_x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.SDL-Debug|experimental_x64.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.SDL-Debug|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.SDL-Debug|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.SDL-Debug|x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.SDL-Debug|x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Debug|experimental_x64.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Debug|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Debug|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Debug|x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Debug|x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release - Fast FPU|experimental_x64.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release - Fast FPU|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release - Fast FPU|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release - Fast FPU|x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release - Fast FPU|x64.Build.0 = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release|experimental_x64.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release|x64.ActiveCfg = Release|x64 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Static lib - Release|x64.Build.0 = Release|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|experimental_x64.ActiveCfg = Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|Win32.ActiveCfg = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|Win32.Build.0 = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|x64.ActiveCfg = Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|x64.Build.0 = Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|experimental_x64.ActiveCfg = Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|Win32.ActiveCfg = Static lib - Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|Win32.Build.0 = Static lib - Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|x64.ActiveCfg = Release - Fast FPU|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU|x64.Build.0 = Release - Fast FPU|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|experimental_x64.ActiveCfg = Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|Win32.ActiveCfg = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|Win32.Build.0 = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|x64.ActiveCfg = Release|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|x64.Build.0 = Release|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.SDL-Debug|experimental_x64.ActiveCfg = SDL-Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.SDL-Debug|Win32.ActiveCfg = SDL-Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.SDL-Debug|Win32.Build.0 = SDL-Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.SDL-Debug|x64.ActiveCfg = SDL-Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.SDL-Debug|x64.Build.0 = SDL-Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Debug|experimental_x64.ActiveCfg = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Debug|Win32.ActiveCfg = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Debug|Win32.Build.0 = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Debug|x64.ActiveCfg = Static lib - Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Debug|x64.Build.0 = Static lib - Debug|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release - Fast FPU|experimental_x64.ActiveCfg = Static lib - Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release - Fast FPU|Win32.ActiveCfg = Static lib - Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release - Fast FPU|Win32.Build.0 = Static lib - Release - Fast FPU|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release - Fast FPU|x64.ActiveCfg = Static lib - Release - Fast FPU|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release - Fast FPU|x64.Build.0 = Static lib - Release - Fast FPU|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|experimental_x64.ActiveCfg = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|Win32.ActiveCfg = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|Win32.Build.0 = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|x64.ActiveCfg = Static lib - Release|x64 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Static lib - Release|x64.Build.0 = Static lib - Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/ide/vc11/supertuxkart.vcxproj b/src/ide/vc11/supertuxkart.vcxproj deleted file mode 100644 index 970c98d05..000000000 --- a/src/ide/vc11/supertuxkart.vcxproj +++ /dev/null @@ -1,737 +0,0 @@ - - - - - debug - Win32 - - - debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {B1BC2764-1A43-4800-A654-788B0D05EDA2} - supertuxkart - Win32Proj - - - - Application - MultiByte - v110 - - - Application - NotSet - v110 - - - Application - MultiByte - true - v110 - - - Application - MultiByte - true - v110 - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - ..\..\../ - ..\..\../ - $(Configuration)\ - $(Configuration)\ - false - false - ..\..\../ - ..\..\../ - $(Configuration)\ - $(Configuration)\ - true - true - $(ProjectName)_d - $(ProjectName)_d - C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include;$(IncludePath) - - - - true - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - HAVE_OGGVORBIS;NDEBUG;_CONSOLE;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;BT_NO_PROFILE;PACKAGE="supertuxkart";ENABLE_BIDI;ADDONS_MANAGER;_IRR_STATIC_LIB_;%(PreprocessorDefinitions) - MultiThreadedDLL - - - Level3 - ProgramDatabase - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;fribidi.lib;pthreadVC2.lib;libcurl_imp.lib;%(AdditionalDependencies) - $(OutDir)/$(ProjectName).exe - ../../../dependencies/lib;../../../lib/irrlicht/lib/Win32-visualstudio;%(AdditionalLibraryDirectories) - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - Console - true - true - false - - - MachineX86 - - - - - true - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - HAVE_OGGVORBIS;NDEBUG;_CONSOLE;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;BT_NO_PROFILE;PACKAGE="supertuxkart";ENABLE_BIDI;WIN64;_IRR_STATIC_LIB_;%(PreprocessorDefinitions) - MultiThreadedDLL - NotUsing - Level3 - ProgramDatabase - StreamingSIMDExtensions2 - true - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;libfribidi.lib;pthreadVC2.lib;libcurl.lib;%(AdditionalDependencies) - $(OutDir)/$(ProjectName).exe - ../../../lib/irrlicht/lib/Win64-visualStudio;../../../dependencies/lib;%(AdditionalLibraryDirectories) - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - Console - true - true - false - - - - - - - Disabled - false - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - BT_NO_PROFILE;HAVE_OGGVORBIS;_DEBUG;_CONSOLE;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;DEBUG;PACKAGE="supertuxkart";_CRTDBG_MAP_ALLOC;ENABLE_BIDI;_IRR_STATIC_LIB_;%(PreprocessorDefinitions):_ITERATOR_DEBUG_LEVEL=0 - false - false - false - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - EditAndContinue - true - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;fribidi.lib;pthreadVC2.lib;libcurl_imp.lib;%(AdditionalDependencies) - $(TargetPath) - ../../../lib/irrlicht/lib/Win32-visualstudio;../../../dependencies/lib;%(AdditionalLibraryDirectories) - false - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - false - - - false - Console - - - false - - - MachineX86 - false - - - - - Disabled - false - ../../../src;../../../lib/bullet/src;../../../lib/enet/include;../../../dependencies/include;../../../lib/irrlicht/include;%(AdditionalIncludeDirectories) - BT_NO_PROFILE;HAVE_OGGVORBIS;_DEBUG;_CONSOLE;WIN32;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;DEBUG;PACKAGE="supertuxkart";_CRTDBG_MAP_ALLOC;ENABLE_BIDI;ADDONS_MANAGER;_WIN64;_IRR_STATIC_LIB_;%(PreprocessorDefinitions) - false - false - EnableFastChecks - MultiThreadedDebugDLL - false - - - Level3 - ProgramDatabase - false - StreamingSIMDExtensions2 - - - opengl32.lib;user32.lib;gdi32.lib;winmm.lib;advapi32.lib;OpenAL32.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;Irrlicht.lib;ws2_32.lib;libfribidi.lib;pthreadVC2.lib;libcurl.lib;%(AdditionalDependencies) - $(TargetPath) - ../../../dependencies/lib;../../../lib/irrlicht/lib/Win64-VisualStudio;%(AdditionalLibraryDirectories) - false - libcmt.lib;%(IgnoreSpecificDefaultLibraries) - true - false - - - false - Console - - - false - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - false - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {54aa44b9-b07b-49da-8b1a-05bbfe8ad5d4} - false - - - {b0e92b97-089a-4d5b-bf17-77f1bc5daeef} - false - - - - - - \ No newline at end of file diff --git a/src/ide/vc11/supertuxkart.vcxproj.filters b/src/ide/vc11/supertuxkart.vcxproj.filters deleted file mode 100644 index 14e1eac46..000000000 --- a/src/ide/vc11/supertuxkart.vcxproj.filters +++ /dev/null @@ -1,1665 +0,0 @@ - - - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {55621666-34fd-482a-b2a0-077db2794fc5} - - - {386e9299-f42e-4175-8f00-c205aa480876} - - - {865587a4-41b6-4832-a961-2f012204ef4c} - - - {1953a99d-3f65-4efe-93d3-b0adb25f970d} - - - {fa2fbc61-28e7-44cc-830d-7d31019b910c} - - - {b231fec4-a731-46b5-a8cf-1ef8fa0bf7be} - - - {06defe0c-00ba-4c91-b0f4-fe0890362f0f} - - - {9f93fb34-c97b-4108-9cd0-3f844d421e11} - - - {6c09fe56-1df1-4fdc-9023-47887316c0b2} - - - {1298df87-da0e-462e-b074-a496598571d8} - - - {11a7d573-5959-4be8-8a3f-d455d7833333} - - - {d477b64b-eb9c-4059-a647-98f3a2f7203a} - - - {2f186570-85e9-4cea-88dc-0f716c6e141a} - - - {f2f3feeb-648b-4edf-8025-fb86e1a6d9bb} - - - {1ea79cd5-39b0-4228-b002-7bddf4d45fda} - - - {24d03539-463f-4c91-9ea0-6fd4b5f1dde0} - - - {24740454-5060-4191-bc49-7ff560db2aba} - - - {03371ee6-3cf3-4ed8-b69e-21a2d0f70351} - - - {62844994-aa74-4a31-b255-1815884856e2} - - - {79bdc1db-0db0-4d69-9e25-c9404a5ab2ee} - - - {fbc2e0f1-a87f-4205-94b4-2a8805338d2c} - - - {533141f3-f1ce-4815-8e9c-4253fd5e9f5e} - - - {5c9f42c5-1a5b-476b-998f-f84384767684} - - - {8da5c73a-5884-4e8d-818a-20521a66e611} - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {88b6497f-6336-4d65-b0e3-5fad9ff35ffb} - - - {c4431a49-640d-48e5-9ac7-473955a15283} - - - {6eeebf67-9364-4c48-9e24-e89164897f9e} - - - {3ee2b9d8-e9b8-4af3-a5bf-28ae532ed5cc} - - - {1bda54ec-85a1-4725-8425-e5942887808d} - - - {0f42eff9-e52c-4975-bc4a-52b0310ddb99} - - - {c3be189e-ac78-42f5-b6e2-7193d9b32e80} - - - {b91bda77-4ae6-4d47-8de1-65b8b992251e} - - - {cd929227-595c-45e8-bf99-ab06e6d586d8} - - - {4bb34709-b7d7-4c3b-9f26-e7a26ffd817a} - - - {8d8a1efe-f3d5-4532-8271-101ed960defc} - - - {45a7521f-d7d3-4404-8f6f-62e2b08c8f4c} - - - {07a426a7-14fb-495b-bff3-5a402664e035} - - - {00e74e62-f481-4883-a49f-661a5b3c9d88} - - - {1e496486-52dd-4fe4-9bd0-3ec0f2b280fc} - - - {7d4fb5af-51c8-4073-bdbf-e0420a211096} - - - {7d4c8018-8cb8-4335-b0db-217883ee8954} - - - {29b448e9-cfd5-4024-a3d7-360ad6d89fed} - - - {305929ad-22c9-4dce-8514-2b3ceab47d2b} - - - {02e2a507-6184-43fb-aa71-46ef3fc77a37} - - - {9ea7a6dd-0714-414c-970c-881b61411ef8} - - - {376d4c71-33a2-4def-949e-31bcb5b72812} - - - {5faa19dc-78e6-438e-a2a0-6a489961f098} - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - - - Source Files - - - Source Files - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\guiengine\widgets - - - Source Files\challenges - - - Source Files\challenges - - - Source Files\challenges - - - Source Files\utils - - - Source Files\utils - - - Source Files\utils - - - Source Files\utils - - - Source Files\utils - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\audio - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\items - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\physics - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts\controller - - - Source Files\karts\controller - - - Source Files\karts\controller - - - Source Files\karts\controller - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\network - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\io - - - Source Files\io - - - Source Files\io - - - Source Files\input - - - Source Files\input - - - Source Files\input - - - Source Files\input - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\race - - - Source Files\config - - - Source Files\config - - - Source Files\config - - - Source Files\animations - - - Source Files\animations - - - Source Files\animations - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\states_screens\dialogs - - - Source Files\addons - - - Source Files\addons - - - Source Files\addons - - - Source Files\addons - - - Source Files\graphics - - - Source Files\physics - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\tinygettext - - - Source Files\graphics - - - Source Files\states_screens - - - Source Files\guiengine\widgets - - - Source Files\addons - - - Source Files\addons - - - Source Files\items - - - Source Files\utils - - - Source Files\graphics - - - Source Files\items - - - Source Files\graphics - - - Source Files\karts - - - Source Files\tracks - - - Source Files\graphics - - - Source Files\graphics - - - Source Files\utils - - - Source Files - - - Source Files\challenges - - - Source Files\states_screens - - - Source Files\states_screens\dialogs - - - Source Files\modes - - - Source Files\states_screens - - - Source Files\karts - - - Source Files\states_screens\dialogs - - - Source Files\replay - - - Source Files\replay - - - Source Files\replay - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\karts - - - Source Files\modes - - - Source Files\config - - - Source Files\tracks - - - Source Files\graphics - - - Source Files\karts\controller - - - Source Files\modes - - - Source Files\states_screens - - - Source Files\addons - - - Source Files\karts\controller - - - Source Files\states_screens - - - Source Files\tinygettext - - - Source Files\states_screens\dialogs - - - Source Files\utils - - - Source Files\modes - - - Source Files\modes - - - Source Files\modes - - - Source Files\states_screens - - - Source Files\tracks - - - Source Files\tracks - - - Source Files\input - - - Source Files - - - Source Files\guiengine\widgets - - - - - Header Files - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\guiengine\widgets - - - Header Files\challenges - - - Header Files\challenges - - - Header Files\challenges - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\network - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\utils - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\audio - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\items - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\physics - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts\controller - - - Header Files\karts\controller - - - Header Files\karts\controller - - - Header Files\karts\controller - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\io - - - Header Files\io - - - Header Files\io - - - Header Files\input - - - Header Files\input - - - Header Files\input - - - Header Files\input - - - Header Files\input - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\race - - - Header Files\config - - - Header Files\config - - - Header Files\config - - - Header Files\config - - - Header Files\animations - - - Header Files\animations - - - Header Files\animations - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\states_screens\dialogs - - - Header Files\addons - - - Header Files\addons - - - Header Files\addons - - - Header Files\addons - - - Header Files\graphics - - - Header Files\physics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\tinygettext - - - Header Files\utils\utf8 - - - Header Files\utils\utf8 - - - Header Files\utils\utf8 - - - Header Files\utils - - - Header Files\graphics - - - Header Files\states_screens - - - Header Files\utils - - - Header Files\guiengine\widgets - - - Header Files\addons - - - Header Files\addons - - - Header Files\items - - - Header Files\utils - - - Header Files\graphics - - - Header Files\items - - - Header Files\graphics - - - Header Files\karts - - - Header Files\tracks - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\graphics - - - Header Files\utils - - - Header Files - - - Header Files\challenges - - - Header Files\states_screens - - - Header Files\states_screens\dialogs - - - Header Files\modes - - - Header Files\states_screens - - - Header Files\karts - - - Header Files\states_screens\dialogs - - - Header Files\replay - - - Header Files\replay - - - Header Files\replay - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\karts - - - Header Files\modes - - - Header Files\tracks - - - Header Files\utils - - - Header Files\graphics - - - Header Files\karts\controller - - - Header Files\modes - - - Header Files\states_screens - - - Header Files\addons - - - Header Files\karts\controller - - - Header Files\utils - - - Header Files\tinygettext - - - Header Files\states_screens\dialogs - - - Header Files\utils - - - Header Files\modes - - - Header Files\modes - - - Header Files\modes - - - Header Files\states_screens - - - Header Files\tracks - - - Header Files\tracks - - - Header Files\input - - - \ No newline at end of file diff --git a/src/ide/vc9/README b/src/ide/vc9/README deleted file mode 100644 index a3e69131e..000000000 --- a/src/ide/vc9/README +++ /dev/null @@ -1,35 +0,0 @@ -Basically 4 setups are included in the vc8-project: ---------------------------------------------------- -The first one is the one currently used and supported, -the others are more 'historical' and will be remove soon. - -BulletDebug/BulletRelease: - main setup (Debug/Relase) + BULLET - The debug version links in the optimised bullet (and sdl and plib) versions, since i is assumed that - mostly STK is being debugged, not everything. Using debug for everything creates a really slow executable. - -ReplayDebug/ReplayRelease: - main setup (Debug/Relase) + HAVE_GHOST_REPLAY - -Debug/Release: - main setup with openAL & vorbis: HAVE_OPENAL + HAVE_OGGVORBIS - -PlibSoundDebug/PlibSoundRelease: - no ogg-sound, no openAL, no vorbis, it is gonna be obsolete, when we move to OpenAL/ogg-vorbis completely - - -Create these environment-vars pointing to the folders of your installations: ----------------------------------------------------------------------------- -STK_LIB: pointing to a directory containing all libs -STK_INCLUDE: pointing to a directory containing all header files. -A separate package will be made available which contains all necessary windows -files to compile STK. If you prefer to have the packages in separate -directories, please add your directories to the paths in visual studio. - -Add the folders of the runtime-dlls to the PATH-variable, this is what i had to put: -(or copy the .dll files in the root directory of supertuxkart). - -Remarks for OpenAL-installation: --------------------------------- -My version of OpenAL 1.1-SDK didn't put its includes in OpenAL/include/AL but in OpenAL/include. -if yours did the same, create the folder OpenAL/include/AL and copy all the includes there. diff --git a/src/ide/vc9/bullet_lib.vcproj b/src/ide/vc9/bullet_lib.vcproj deleted file mode 100644 index 62640a199..000000000 --- a/src/ide/vc9/bullet_lib.vcproj +++ /dev/null @@ -1,1662 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ide/vc9/enet.vcproj b/src/ide/vc9/enet.vcproj deleted file mode 100644 index 2d4f71ade..000000000 --- a/src/ide/vc9/enet.vcproj +++ /dev/null @@ -1,426 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ide/vc9/supertuxkart.sln b/src/ide/vc9/supertuxkart.sln deleted file mode 100644 index d6a3108d1..000000000 --- a/src/ide/vc9/supertuxkart.sln +++ /dev/null @@ -1,68 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "supertuxkart", "supertuxkart.vcproj", "{B1BC2764-1A43-4800-A654-788B0D05EDA2}" - ProjectSection(ProjectDependencies) = postProject - {E08E042A-6C45-411B-92BE-3CC31331019F} = {E08E042A-6C45-411B-92BE-3CC31331019F} - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF} = {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF} - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4} = {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bullet_lib", "bullet_lib.vcproj", "{54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enet", "enet.vcproj", "{B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Irrlicht", "..\..\..\lib\irrlicht\source\Irrlicht\Irrlicht9.0.vcproj", "{E08E042A-6C45-411B-92BE-3CC31331019F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wiiuse", "..\..\..\lib\wiiuse\wiiuse\wiiuse.vcproj", "{FE4FCEBF-B53D-4B8A-81B0-F9AB059C8C83}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - debug|Win32 = debug|Win32 - Release|Win32 = Release|Win32 - wiiuse-debug|Win32 = wiiuse-debug|Win32 - wiiuse-release|Win32 = wiiuse-release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|Win32.ActiveCfg = Debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.debug|Win32.Build.0 = Debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|Win32.ActiveCfg = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.Release|Win32.Build.0 = Release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.wiiuse-debug|Win32.ActiveCfg = wiiuse-debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.wiiuse-debug|Win32.Build.0 = wiiuse-debug|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.wiiuse-release|Win32.ActiveCfg = wiiuse-release|Win32 - {B1BC2764-1A43-4800-A654-788B0D05EDA2}.wiiuse-release|Win32.Build.0 = wiiuse-release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|Win32.ActiveCfg = Debug|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.debug|Win32.Build.0 = Debug|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.Release|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.wiiuse-debug|Win32.ActiveCfg = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.wiiuse-debug|Win32.Build.0 = Release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.wiiuse-release|Win32.ActiveCfg = wiiuse-release|Win32 - {54AA44B9-B07B-49DA-8B1A-05BBFE8AD5D4}.wiiuse-release|Win32.Build.0 = wiiuse-release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|Win32.ActiveCfg = Debug|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|Win32.Build.0 = Debug|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-debug|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-debug|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-release|Win32.ActiveCfg = wiiuse-release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-release|Win32.Build.0 = wiiuse-release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|Win32.ActiveCfg = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|Win32.Build.0 = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|Win32.ActiveCfg = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.Release|Win32.Build.0 = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.wiiuse-debug|Win32.ActiveCfg = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.wiiuse-debug|Win32.Build.0 = Static lib - Debug|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.wiiuse-release|Win32.ActiveCfg = Static lib - Release|Win32 - {E08E042A-6C45-411B-92BE-3CC31331019F}.wiiuse-release|Win32.Build.0 = Static lib - Release|Win32 - {FE4FCEBF-B53D-4B8A-81B0-F9AB059C8C83}.debug|Win32.ActiveCfg = Debug|Win32 - {FE4FCEBF-B53D-4B8A-81B0-F9AB059C8C83}.Release|Win32.ActiveCfg = Release|Win32 - {FE4FCEBF-B53D-4B8A-81B0-F9AB059C8C83}.wiiuse-debug|Win32.ActiveCfg = Debug|Win32 - {FE4FCEBF-B53D-4B8A-81B0-F9AB059C8C83}.wiiuse-release|Win32.ActiveCfg = wiiuse-release|Win32 - {FE4FCEBF-B53D-4B8A-81B0-F9AB059C8C83}.wiiuse-release|Win32.Build.0 = wiiuse-release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/ide/vc9/supertuxkart.vcproj b/src/ide/vc9/supertuxkart.vcproj deleted file mode 100644 index 0ba441b63..000000000 --- a/src/ide/vc9/supertuxkart.vcproj +++ /dev/null @@ -1,2845 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/input/binding.cpp b/src/input/binding.cpp index 683199a97..dcac002ea 100644 --- a/src/input/binding.cpp +++ b/src/input/binding.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/input/binding.hpp b/src/input/binding.hpp index 2af59f154..0362f2d05 100644 --- a/src/input/binding.hpp +++ b/src/input/binding.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/input/device_manager.cpp b/src/input/device_manager.cpp index e01c54388..44baadbbd 100644 --- a/src/input/device_manager.cpp +++ b/src/input/device_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/input/device_manager.hpp b/src/input/device_manager.hpp index 34d097421..c18a5b43f 100644 --- a/src/input/device_manager.hpp +++ b/src/input/device_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -156,7 +156,7 @@ public: StateManager::ActivePlayer** player /* out */, PlayerAction* action /* out */ ); - void clearLatestUsedDevice(); + void clearLatestUsedDevice(); InputDevice* getLatestUsedDevice(); bool initialize(); void serialize(); diff --git a/src/input/input.hpp b/src/input/input.hpp index c911e2dcd..26366ebc6 100644 --- a/src/input/input.hpp +++ b/src/input/input.hpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007-2008 Robert Schuster +// Copyright (C) 2007-2013 Robert Schuster +// Copyright (C) 2012-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/input/input_device.cpp b/src/input/input_device.cpp index b1ecfcebf..f6bb5c3fb 100644 --- a/src/input/input_device.cpp +++ b/src/input/input_device.cpp @@ -1,3 +1,20 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2009-2013 Marianne Gagnon +// +// 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 "config/device_config.hpp" #include "guiengine/abstract_state_manager.hpp" diff --git a/src/input/input_device.hpp b/src/input/input_device.hpp index fc28da1de..17ab816ed 100644 --- a/src/input/input_device.hpp +++ b/src/input/input_device.hpp @@ -1,3 +1,21 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2009-2013 Marianne Gagnon +// +// 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 INPUT_DEVICE_HPP #define INPUT_DEVICE_HPP diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index 4c7badc31..81705158b 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -1,6 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart // +// Copyright (C) 2012-2013 SuperTuxKart-Team +// // 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 @@ -289,15 +290,51 @@ void InputManager::handleStaticAction(int key, int value) UserConfigParams::m_profiler_enabled = !UserConfigParams::m_profiler_enabled; break; + + // Debug views + // These should be available in normal (non-artist) mode + // to enable easier bug reports (go there, press this key, screenshot) + // without requiring editing the UTF-32 config files (which is inconvenient). case KEY_HOME: if (value) { - video::SOverrideMaterial &mat = - irr_driver->getVideoDriver()->getOverrideMaterial(); - - mat.Material.Wireframe ^= 1; - mat.EnableFlags = video::EMF_WIREFRAME; - mat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT; + irr_driver->toggleWireframe(); + } + break; + case KEY_END: + if (value) + { + irr_driver->toggleMipVisualization(); + } + break; + case KEY_DELETE: + if (value) + { + irr_driver->toggleNormals(); + } + break; + case KEY_NEXT: // pgdown + if (value) + { + irr_driver->toggleSSAOViz(); + } + break; + case KEY_PRIOR: // pgup + if (value) + { + irr_driver->toggleLightViz(); + } + break; + case KEY_INSERT: + if (value) + { + irr_driver->toggleShadowViz(); + } + break; + case KEY_SCROLL: + if (value) + { + irr_driver->toggleDistortViz(); } break; default: @@ -558,7 +595,7 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, action == PA_MENU_CANCEL ) ) { // returns true if the event was handled - if (KartSelectionScreen::getInstance()->playerQuit( player )) + if (KartSelectionScreen::getRunningInstance()->playerQuit( player )) { return; // we're done here } @@ -593,7 +630,7 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, if (device != NULL) { - KartSelectionScreen::getInstance()->playerJoin(device, + KartSelectionScreen::getRunningInstance()->playerJoin(device, false ); } } diff --git a/src/input/input_manager.hpp b/src/input/input_manager.hpp index 6cf357612..19069efe8 100644 --- a/src/input/input_manager.hpp +++ b/src/input/input_manager.hpp @@ -1,6 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart +// // Copyright (C) 2004-2006 Steve Baker +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/input/wiimote.cpp b/src/input/wiimote.cpp index 1c786619b..d27c6b38c 100644 --- a/src/input/wiimote.cpp +++ b/src/input/wiimote.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 SuperTuxKart-Team +// Copyright (C) 2013-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -79,16 +79,6 @@ void Wiimote::update() float normalized_angle = -(m_wiimote_handle->accel.y-128) / UserConfigParams::m_wiimote_raw_max; -#ifdef DEBUG - if(UserConfigParams::m_wiimote_debug) - { - Log::verbose("wiimote", "xyz %d %d %d %f", - m_wiimote_handle->accel.x, - m_wiimote_handle->accel.y, - m_wiimote_handle->accel.z, - normalized_angle); - } -#endif if(normalized_angle<-1.0f) normalized_angle = -1.0f; else if(normalized_angle>1.0f) @@ -105,8 +95,17 @@ void Wiimote::update() const float sign = normalized_angle >= 0.0f ? 1.0f : -1.0f; const float normalized_angle_2 = w1 * normalized_angle + w2 * sign*normalized_angle*normalized_angle - + wa * asin(normalized_angle)*(2.0f/M_PI) + + wa * asin(normalized_angle)*(2.0f/M_PI) + ws * sin(normalized_angle*(M_PI/2.0f)); + + if(UserConfigParams::m_wiimote_debug) + { + Log::verbose("wiimote", "raw %d normal %f result %f", + m_wiimote_handle->accel.y, + normalized_angle, + normalized_angle_2); + } + const float JOYSTICK_ABS_MAX_ANGLE = 32766.0f; const float angle = normalized_angle_2 * JOYSTICK_ABS_MAX_ANGLE; diff --git a/src/input/wiimote.hpp b/src/input/wiimote.hpp index 1eae85de9..60e29eab5 100644 --- a/src/input/wiimote.hpp +++ b/src/input/wiimote.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 SuperTuxKart-Team +// Copyright (C) 2013-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/input/wiimote_manager.cpp b/src/input/wiimote_manager.cpp index 440b98448..73cf2bbcd 100644 --- a/src/input/wiimote_manager.cpp +++ b/src/input/wiimote_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 SuperTuxKart-Team +// Copyright (C) 2012-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -26,6 +26,7 @@ #include "input/device_manager.hpp" #include "input/wiimote.hpp" #include "utils/string_utils.hpp" +#include "utils/time.hpp" #include "utils/translation.hpp" #include "wiiuse.h" @@ -110,7 +111,7 @@ void WiimoteManager::launchDetection(int timeout) wiiuse_rumble(wiimote_handle, 1); } - irr_driver->getDevice()->sleep(200); + StkTime::sleep(200); for(unsigned int i=0 ; i < m_wiimotes.size(); i++) { @@ -284,7 +285,7 @@ void WiimoteManager::threadFunc() } } - irr_driver->getDevice()->sleep(1); // 'cause come on, the whole CPU is not ours :) + StkTime::sleep(1); // 'cause come on, the whole CPU is not ours :) } // end while } // threadFunc @@ -304,19 +305,19 @@ void* WiimoteManager::threadFuncWrapper(void *data) */ int WiimoteManager::askUserToConnectWiimotes() { - new MessageDialog( + new MessageDialog( #ifdef WIN32 - _("Connect your wiimote to the Bluetooth manager, then click on Ok." + _("Connect your wiimote to the Bluetooth manager, then click on Ok." "Detailed instructions at supertuxkart.net/Wiimote"), #else - _("Press the buttons 1+2 simultaneously on your wiimote to put " - "it in discovery mode, then click on Ok." + _("Press the buttons 1+2 simultaneously on your wiimote to put " + "it in discovery mode, then click on Ok." "Detailed instructions at supertuxkart.net/Wiimote"), #endif - MessageDialog::MESSAGE_DIALOG_OK_CANCEL, - new WiimoteDialogListener(), true); + MessageDialog::MESSAGE_DIALOG_OK_CANCEL, + new WiimoteDialogListener(), true); - return getNumberOfWiimotes(); + return getNumberOfWiimotes(); } // askUserToConnectWiimotes // ============================================================================ @@ -325,23 +326,23 @@ int WiimoteManager::askUserToConnectWiimotes() */ void WiimoteManager::WiimoteDialogListener::onConfirm() { - GUIEngine::ModalDialog::dismiss(); + GUIEngine::ModalDialog::dismiss(); - wiimote_manager->launchDetection(5); + wiimote_manager->launchDetection(5); - int nb_wiimotes = wiimote_manager->getNumberOfWiimotes(); - if(nb_wiimotes > 0) - { - core::stringw msg = StringUtils::insertValues( - _("Found %d wiimote(s)"), - core::stringw(nb_wiimotes)); + int nb_wiimotes = wiimote_manager->getNumberOfWiimotes(); + if(nb_wiimotes > 0) + { + core::stringw msg = StringUtils::insertValues( + _("Found %d wiimote(s)"), + core::stringw(nb_wiimotes)); - new MessageDialog( msg ); + new MessageDialog( msg ); - } - else - { - new MessageDialog( _("Could not detect any wiimote :/") ); - } + } + else + { + new MessageDialog( _("Could not detect any wiimote :/") ); + } } // WiimoteDialogListeneronConfirm #endif // ENABLE_WIIUSE diff --git a/src/input/wiimote_manager.hpp b/src/input/wiimote_manager.hpp index 75dd7c3d3..e701472ec 100644 --- a/src/input/wiimote_manager.hpp +++ b/src/input/wiimote_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 SuperTuxKart-Team +// Copyright (C) 2012-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -63,8 +63,8 @@ private: bool m_shut; #endif - /** True if wii is enabled via command line option. */ - static bool m_enabled; + /** True if wii is enabled via command line option. */ + static bool m_enabled; /** Wiimotes update thread */ void threadFunc(); @@ -75,11 +75,11 @@ public: WiimoteManager(); ~WiimoteManager(); - /** Sets the wiimote to be enabled. */ - static void enable() { m_enabled = true; } + /** Sets the wiimote to be enabled. */ + static void enable() { m_enabled = true; } - /** Returns if the wii was enabled on the command line. */ - static bool isEnabled() { return m_enabled; } + /** Returns if the wii was enabled on the command line. */ + static bool isEnabled() { return m_enabled; } void launchDetection(int timeout); void update(); @@ -87,20 +87,20 @@ public: void enableAccelerometer(bool state); - /** A simple listener to allow the user to connect wiimotes. It - * will display a feedback windows (# wiimotes connected or 'no wiimotes - * found'). - */ - class WiimoteDialogListener : public MessageDialog::IConfirmDialogListener - { - public: - virtual void onConfirm() OVERRIDE; - }; // class WiimoteDialoListener + /** A simple listener to allow the user to connect wiimotes. It + * will display a feedback windows (# wiimotes connected or 'no wiimotes + * found'). + */ + class WiimoteDialogListener : public MessageDialog::IConfirmDialogListener + { + public: + virtual void onConfirm() OVERRIDE; + }; // class WiimoteDialoListener - /** Shows a dialog allowing the user to connect wiimotes. - * \return Number of wiimotes connected. - */ - int askUserToConnectWiimotes(); + /** Shows a dialog allowing the user to connect wiimotes. + * \return Number of wiimotes connected. + */ + int askUserToConnectWiimotes(); // ------------------------------------------------------------------------ /** Returns the number of wiimotes connected. */ unsigned int getNumberOfWiimotes() const {return m_wiimotes.size();} diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp index 478c13f1f..c8543e1bb 100644 --- a/src/io/file_manager.cpp +++ b/src/io/file_manager.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2004 Steve Baker -// (C) 2008 Steve Baker, Joerg Henrichs +// Copyright (C) 2008-2013 Steve Baker, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -293,7 +293,33 @@ XMLNode *FileManager::createXMLTree(const std::string &filename) } return NULL; } -} // getXMLTree +} // createXMLTree + +//----------------------------------------------------------------------------- +/** Reads in XML from a string and converts it into a XMLNode tree. + * \param content the string containing the XML content. + */ +XMLNode *FileManager::createXMLTreeFromString(const std::string & content) +{ + try + { + char *b = new char[content.size()]; + memcpy(b, content.c_str(), content.size()); + io::IReadFile * ireadfile = m_file_system->createMemoryReadFile(b, strlen(b), "tempfile", true); + io::IXMLReader * reader = m_file_system->createXMLReader(ireadfile); + XMLNode* node = new XMLNode(reader); + reader->drop(); + return node; + } + catch (std::runtime_error& e) + { + if (UserConfigParams::logMisc()) + { + Log::error("FileManager", "createXMLTreeFromString: %s\n", e.what()); + } + return NULL; + } +} // createXMLTreeFromString //----------------------------------------------------------------------------- /** In order to add and later remove paths we have to specify the absolute @@ -818,7 +844,7 @@ std::string FileManager::checkAndCreateLinuxDir(const char *env_name, void FileManager::redirectOutput() { //Enable logging of stdout and stderr to logfile - std::string logoutfile = getLogFile("stdout.log"); + std::string logoutfile = getConfigFile("stdout.log"); Log::verbose("main", "Error messages and other text output will " "be logged to %s.", logoutfile.c_str()); Log::openOutputFiles(logoutfile); @@ -848,16 +874,6 @@ std::string FileManager::getConfigDir() const return m_config_dir; } // getConfigDir -//----------------------------------------------------------------------------- -/** Returns the full path of a file in the user config directory which is - * used to store stdout/stderr if it is redirected. - * \param name Name of the file. - */ -std::string FileManager::getLogFile(const std::string& file_name) const -{ - return getConfigDir()+"/"+file_name; -} // getLogFile - //----------------------------------------------------------------------------- /** Returns the full path of a music file by searching all music search paths. * It throws an exception if the file is not found. @@ -895,33 +911,14 @@ std::string FileManager::getFontFile(const std::string& file_name) const } // getFontFile //----------------------------------------------------------------------------- -/** Returns the full path of a highscore file (which is stored in the user - * specific config directory). - * \param file_name Name of the sound effect file. - */ -std::string FileManager::getHighscoreFile(const std::string& file_name) const -{ - return getConfigDir()+"/"+file_name; -} // getHighscoreFile - -//----------------------------------------------------------------------------- -/** Returns the full path of the challenge file (which is stored in a user - * specific config area). +/** Returns the full path of a file in the config dir * \param file_name Name of the file. */ -std::string FileManager::getChallengeFile(const std::string &file_name) const +std::string FileManager::getConfigFile(const std::string &file_name) const { - return getConfigDir()+"/"+file_name; + return getConfigDir()+file_name; } // getChallengeFile -//----------------------------------------------------------------------------- -/** Returns the full path of the tutorial file. - * \param file_name Name of the tutorial file to return. - */ -std::string FileManager::getTutorialFile(const std::string &file_name) const -{ - return getConfigDir()+"/"+file_name; -} // getTutorialFile //----------------------------------------------------------------------------- /** Returns true if the given name is a directory. diff --git a/src/io/file_manager.hpp b/src/io/file_manager.hpp index 132f7c17d..ebe8d8ef5 100644 --- a/src/io/file_manager.hpp +++ b/src/io/file_manager.hpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2004 Steve Baker +// Copyright (C) 2008-2013 Steve Baker, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -88,6 +89,7 @@ public: void dropFileSystem(); io::IXMLReader *createXMLReader(const std::string &filename); XMLNode *createXMLTree(const std::string &filename); + XMLNode *createXMLTreeFromString(const std::string & content); std::string getConfigDir() const; std::string getTextureDir() const; @@ -105,10 +107,7 @@ public: std::vectorgetMusicDirs() const; std::string getTextureFile (const std::string& fname) const; std::string getDataFile (const std::string& fname) const; - std::string getHighscoreFile (const std::string& fname) const; - std::string getChallengeFile (const std::string& fname) const; - std::string getTutorialFile (const std::string& fname) const; - std::string getLogFile (const std::string& fname) const; + std::string getConfigFile (const std::string& fname) const; std::string getItemFile (const std::string& fname) const; std::string getGfxFile (const std::string& fname) const; std::string getMusicFile (const std::string& fname) const; diff --git a/src/io/xml_node.cpp b/src/io/xml_node.cpp index 84bc8448c..d78dc379f 100644 --- a/src/io/xml_node.cpp +++ b/src/io/xml_node.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ XMLNode::XMLNode(const std::string &filename) m_file_name = filename; io::IXMLReader *xml = file_manager->createXMLReader(filename); - + if (xml == NULL) { throw std::runtime_error("Cannot find file "+filename); @@ -319,6 +319,22 @@ int XMLNode::get(const std::string &attribute, int64_t *value) const } // get(int64_t) +// ---------------------------------------------------------------------------- +int XMLNode::get(const std::string &attribute, uint16_t *value) const +{ + std::string s; + if(!get(attribute, &s)) return 0; + + if (!StringUtils::parseString(s, value)) + { + fprintf(stderr, "[XMLNode] WARNING: Expected uint but found '%s' for attribute '%s' of node '%s' in file %s\n", + s.c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str()); + return 0; + } + + return 1; +} // get(uint32_t) + // ---------------------------------------------------------------------------- int XMLNode::get(const std::string &attribute, uint32_t *value) const { diff --git a/src/io/xml_node.hpp b/src/io/xml_node.hpp index 0e8642d69..bbd0ebe9a 100644 --- a/src/io/xml_node.hpp +++ b/src/io/xml_node.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,15 +19,6 @@ #ifndef HEADER_XML_NODE_HPP #define HEADER_XML_NODE_HPP -#ifdef _MSC_VER - typedef __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; -#else -# include -#endif - #include #include #include @@ -43,6 +34,7 @@ using namespace irr; #include "utils/leak_check.hpp" #include "utils/no_copy.hpp" #include "utils/time.hpp" +#include "utils/types.hpp" class InterpolationArray; class Vec3; @@ -82,6 +74,7 @@ public: int get(const std::string &attribute, std::string *value) const; int get(const std::string &attribute, core::stringw *value) const; int get(const std::string &attribute, int32_t *value) const; + int get(const std::string &attribute, uint16_t *value) const; int get(const std::string &attribute, uint32_t *value) const; int get(const std::string &attribute, int64_t *value) const; int get(const std::string &attribute, float *value) const; diff --git a/src/io/xml_writer.cpp b/src/io/xml_writer.cpp index dca8f59e2..bc9443414 100644 --- a/src/io/xml_writer.cpp +++ b/src/io/xml_writer.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/io/xml_writer.hpp b/src/io/xml_writer.hpp index 474c8791f..3fcb9132b 100644 --- a/src/io/xml_writer.hpp +++ b/src/io/xml_writer.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index f9ae8fb59..c308d2d24 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,8 +34,8 @@ #include "karts/kart_properties.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" -#include "network/race_state.hpp" -#include "network/network_manager.hpp" +#include "physics/triangle_mesh.hpp" +#include "tracks/track.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -177,6 +177,8 @@ void Attachment::set(AttachmentType type, float time, } } m_node->setVisible(true); + + irr_driver->applyObjectPassShader(m_node); } // set // ----------------------------------------------------------------------------- @@ -281,15 +283,6 @@ void Attachment::hitBanana(Item *item, int new_attachment) new_attachment = m_random.get(3); } // switch - // Save the information about the attachment in the race state - // so that the clients can be updated. - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - race_state->itemCollected(m_kart->getWorldKartId(), - item->getItemId(), - new_attachment); - } - if (add_a_new_item) { switch (new_attachment) diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 222e91dbf..6fa469562 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/attachment_manager.cpp b/src/items/attachment_manager.cpp index dec965696..a3121e8d5 100644 --- a/src/items/attachment_manager.cpp +++ b/src/items/attachment_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/attachment_manager.hpp b/src/items/attachment_manager.hpp index fbb41c19f..92bc6cba9 100644 --- a/src/items/attachment_manager.hpp +++ b/src/items/attachment_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/attachment_plugin.hpp b/src/items/attachment_plugin.hpp index cb006ceb2..1b90a6ef1 100644 --- a/src/items/attachment_plugin.hpp +++ b/src/items/attachment_plugin.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index bb1817a91..b7fe73699 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -110,51 +110,6 @@ void Bowling::init(const XMLNode &node, scene::IMesh *bowling) node.get("force-to-target", &m_st_force_to_target); } // init -// ---------------------------------------------------------------------------- -/** Picks a random message to be displayed when a kart is hit by a bowling - * ball. This function picks a different message if a kart hit itself. - * \param kart The kart that was hit. - * \returns The string to display. - */ -const core::stringw Bowling::getHitString(const AbstractKart *kart) const -{ - RandomGenerator r; - - if(kart!=m_owner) - { - const int BOWLING_STRINGS_AMOUNT = 3; - switch (r.get(BOWLING_STRINGS_AMOUNT)) - { - //I18N: shown when hit by bowling ball. %1 is the attacker, %0 is - // the victim. - case 0 : return _LTR("%0 will not go bowling with %1 again"); - //I18N: shown when hit by bowling ball. %1 is the attacker, %0 is - // the victim. - case 1 : return _LTR("%1 strikes %0"); - //I18N: shown when hit by bowling ball. %1 is the attacker, %0 is - // the victim. - case 2 : return _LTR("%0 is bowled over by %1"); - default: assert(false); return L""; // avoid compiler warning - } - } - else - { - const int SELFBOWLING_STRINGS_AMOUNT = 3; - switch (r.get(SELFBOWLING_STRINGS_AMOUNT)) - { - //I18N: shown when hit by own bowling ball. %s is the kart. - case 0 : return _LTR("%s is practicing with a blue, big, spheric yo-yo"); - //I18N: shown when hit by own bowling ball. %s is the kart. - case 1 : return _LTR("%s is the world master of the boomerang ball"); - //I18N: shown when hit by own bowling ball. %s is the kart. - case 2 : return _LTR("%s should play (rubber) darts instead of bowling"); - default: assert(false); return L""; // avoid compiler warning - } // switch - } // if kart_hit==owner - - -} // getHitString - // ---------------------------------------------------------------------------- /** Updates the bowling ball ineach frame. If this function returns true, the * object will be removed by the projectile manager. diff --git a/src/items/bowling.hpp b/src/items/bowling.hpp index f8ef002c6..8b84c3f3b 100644 --- a/src/items/bowling.hpp +++ b/src/items/bowling.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2007-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -54,7 +54,6 @@ public: virtual ~Bowling(); static void init(const XMLNode &node, scene::IMesh *bowling); virtual bool updateAndDelete(float dt); - virtual const core::stringw getHitString(const AbstractKart *kart) const; virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); virtual HitEffect *getHitEffect() const; diff --git a/src/items/cake.cpp b/src/items/cake.cpp index e7e1a4fdf..7f802dcbb 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -1,9 +1,9 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // Physics improvements and linear intersection algorithm by -// by David Mikos. Copyright (C) 2009. +// Copyright (C) 2009-2013 David Mikos. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -136,28 +136,6 @@ void Cake::init(const XMLNode &node, scene::IMesh *cake_model) m_st_max_distance_squared = max_distance*max_distance; } // init -// ---------------------------------------------------------------------------- -/** Picks a random message to be displayed when a kart is hit by a cake. - * \param The kart that was hit (ignored here). - * \returns The string to display. - */ -const core::stringw Cake::getHitString(const AbstractKart *kart) const -{ - const int CAKE_STRINGS_AMOUNT = 4; - RandomGenerator r; - switch (r.get(CAKE_STRINGS_AMOUNT)) - { - //I18N: shown when hit by cake. %1 is the attacker, %0 is the victim. - case 0: return _LTR("%0 eats too much of %1's cake"); - //I18N: shown when hit by cake. %1 is the attacker, %0 is the victim. - case 1: return _LTR("%0 is dubious of %1's cooking skills"); - //I18N: shown when hit by cake. %1 is the attacker, %0 is the victim. - case 2: return _LTR("%0 should not play with %1's lunch"); - //I18N: shown when hit by cake. %1 is the attacker, %0 is the victim. - case 3: return _LTR("%1 ruins %0's cakeless diet"); - default: assert(false); return L""; // avoid compiler warning - } -} // getHitString // ---------------------------------------------------------------------------- /** Callback from the physics in case that a kart or physical object is hit. * The cake triggers an explosion when hit. diff --git a/src/items/cake.hpp b/src/items/cake.hpp index 111a74cb8..626a671e6 100644 --- a/src/items/cake.hpp +++ b/src/items/cake.hpp @@ -1,9 +1,9 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // Physics improvements and linear intersection algorithm by -// by David Mikos. Copyright (C) 2009. +// Copyright (C) 2009-2013 David Mikos. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -49,8 +49,6 @@ private: public: Cake (AbstractKart *kart); static void init (const XMLNode &node, scene::IMesh *cake_model); - virtual const core::stringw - getHitString(const AbstractKart *kart) const; virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); // ------------------------------------------------------------------------ virtual void hitTrack () { hit(NULL); } diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index ed35ac2af..9d5525075 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -1,9 +1,9 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // Linear item-kart intersection function written by -// by David Mikos. Copyright (C) 2009. +// Copyright (C) 2009-2013 David Mikos // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,9 +21,6 @@ #include "items/flyable.hpp" -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#endif #include #include @@ -38,11 +35,11 @@ #include "karts/abstract_kart.hpp" #include "karts/explosion_animation.hpp" #include "modes/world.hpp" -#include "network/flyable_info.hpp" #include "physics/physics.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/string_utils.hpp" +#include "utils/vs.hpp" // static variables: float Flyable::m_st_speed [PowerupManager::POWERUP_MAX]; @@ -78,6 +75,7 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type, // Add the graphical model setNode(irr_driver->addMesh(m_st_model[type])); + irr_driver->applyObjectPassShader(getNode()); #ifdef DEBUG std::string debug_name("flyable: "); debug_name += type; @@ -410,19 +408,6 @@ bool Flyable::updateAndDelete(float dt) return false; } // updateAndDelete -// ---------------------------------------------------------------------------- -/** Updates the position of a projectile based on information received frmo the - * server. - */ -void Flyable::updateFromServer(const FlyableInfo &f, float dt) -{ - setXYZ(f.m_xyz); - setRotation(f.m_rotation); - - // Update the graphical position - Moveable::update(dt); -} // updateFromServer - // ---------------------------------------------------------------------------- /** Returns true if the item hit the kart who shot it (to avoid that an item * that's too close to the shooter hits the shooter). @@ -446,20 +431,6 @@ bool Flyable::hit(AbstractKart *kart_hit, PhysicalObject* object) { // the owner of this flyable should not be hit by his own flyable if(isOwnerImmunity(kart_hit)) return false; - - if (kart_hit != NULL) - { //TODO: reduce shield time; add other string ? - RaceGUIBase* gui = World::getWorld()->getRaceGUI(); - irr::core::stringw hit_message = - StringUtils::insertValues(getHitString(kart_hit), - core::stringw(kart_hit->getName()), - core::stringw(m_owner ->getName()) - ); - if(hit_message.size()>0) - gui->addMessage(translations->fribidize(hit_message), NULL, 3.0f, - video::SColor(255, 255, 255, 255), false); - } - m_has_hit_something=true; return true; diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index b10960aa9..e36381d6e 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -1,9 +1,9 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // Linear item-kart intersection function written by -// by David Mikos. Copyright (C) 2009. +// Copyright (C) 2009-2013 David Mikos. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,7 +34,6 @@ using namespace irr; #include "tracks/terrain_info.hpp" class AbstractKart; -class FlyableInfo; class HitEffect; class PhysicalObject; class XMLNode; @@ -167,9 +166,7 @@ public: static void init (const XMLNode &node, scene::IMesh *model, PowerupManager::PowerupType type); virtual bool updateAndDelete(float); - virtual const core::stringw getHitString(const AbstractKart *kart) const = 0; virtual HitEffect* getHitEffect() const; - void updateFromServer(const FlyableInfo &f, float dt); bool isOwnerImmunity(const AbstractKart *kart_hit) const; virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); void explode(AbstractKart* kart, PhysicalObject* obj=NULL, diff --git a/src/items/item.cpp b/src/items/item.cpp index 0713fa85a..2a15d4399 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -1,6 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart +// // Copyright (C) 2004 Steve Baker +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -110,8 +111,7 @@ 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) && - (type!=ITEM_EASTER_EGG); + m_rotate = (type!=ITEM_BUBBLEGUM) && (type!=ITEM_TRIGGER); switch(m_type) { case ITEM_BUBBLEGUM: @@ -186,8 +186,11 @@ void Item::switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh) { node = m_node->getAllNodes()[1]; ((scene::IMeshSceneNode*)node)->setMesh(lowmesh); + irr_driver->applyObjectPassShader(m_node->getAllNodes()[1]); } + irr_driver->applyObjectPassShader(m_node->getAllNodes()[0]); + World::getWorld()->getTrack()->adjustForFog(m_node); } // switchTo diff --git a/src/items/item.hpp b/src/items/item.hpp index 40aedad00..df6125197 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -1,6 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart +// // Copyright (C) 2004 Steve Baker +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 161f5ca98..761cddd3e 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -30,6 +30,7 @@ #include "karts/abstract_kart.hpp" #include "modes/linear_world.hpp" #include "network/network_manager.hpp" +#include "network/network_world.hpp" #include "tracks/quad_graph.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" @@ -293,9 +294,6 @@ void ItemManager::collectedItem(Item *item, AbstractKart *kart, int add_info) */ void ItemManager::checkItemHit(AbstractKart* kart) { - // Only do this on the server - if(network_manager->getMode()==NetworkManager::NW_CLIENT) return; - // We could use m_items_in_quads to to check for item hits: take the quad // of the graph node of the kart, and only check items in that quad. But // then we also need to check for any adjacent quads (since an item just @@ -313,7 +311,14 @@ void ItemManager::checkItemHit(AbstractKart* kart) // we pass the kart and the position separately. if((*i)->hitKart(kart->getXYZ(), kart)) { - collectedItem(*i, kart); + // if we're not playing online, pick the item. + if (!NetworkWorld::getInstance()->isRunning()) + collectedItem(*i, kart); + else if (NetworkManager::getInstance()->isServer()) + { + collectedItem(*i, kart); + NetworkWorld::getInstance()->collectedItem(*i, kart); + } } // if hit } // for m_all_items } // checkItemHit diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index 90ba4e36c..a69736153 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/plunger.cpp b/src/items/plunger.cpp index 208386f8d..ca7ea56f0 100644 --- a/src/items/plunger.cpp +++ b/src/items/plunger.cpp @@ -1,9 +1,9 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // Physics improvements and linear intersection algorithm by -// by David Mikos. Copyright (C) 2009. +// Copyright (C) 2009-2013 David Mikos. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -118,25 +118,6 @@ void Plunger::init(const XMLNode &node, scene::IMesh *plunger_model) Flyable::init(node, plunger_model, PowerupManager::POWERUP_PLUNGER); } // init -// ---------------------------------------------------------------------------- -/** Picks a random message to be displayed when a kart is hit by a plunger. - * \param The kart that was hit (ignored here). - * \returns The string to display. - */ -const core::stringw Plunger::getHitString(const AbstractKart *kart) const -{ - const int PLUNGER_IN_FACE_STRINGS_AMOUNT = 2; - RandomGenerator r; - switch (r.get(PLUNGER_IN_FACE_STRINGS_AMOUNT)) - { - //I18N: shown when a player receives a plunger in his face - case 0: return _LTR("%0 gets a fancy mask from %1"); - //I18N: shown when a player receives a plunger in his face - case 1: return _LTR("%1 merges %0's face with a plunger"); - default:assert(false); return L""; // avoid compiler warning - } -} // getHitString - // ---------------------------------------------------------------------------- /** Updates the bowling ball ineach frame. If this function returns true, the * object will be removed by the projectile manager. @@ -180,9 +161,6 @@ bool Plunger::hit(AbstractKart *kart, PhysicalObject *obj) { if(isOwnerImmunity(kart)) return false; - RaceGUIBase* gui = World::getWorld()->getRaceGUI(); - irr::core::stringw hit_message; - // pulling back makes no sense in battle mode, since this mode is not a race. // so in battle mode, always hide view if( m_reverse_mode || race_manager->isBattleMode() ) @@ -192,13 +170,6 @@ bool Plunger::hit(AbstractKart *kart, PhysicalObject *obj) kart->blockViewWithPlunger(); if (kart->getController()->isPlayerController()) sfx_manager->quickSound("plunger"); - - hit_message += StringUtils::insertValues(getHitString(kart), - core::stringw(kart->getName()), - core::stringw(m_owner->getName()) - ).c_str(); - gui->addMessage(translations->fribidize(hit_message), NULL, 3.0f, - video::SColor(255, 255, 255, 255), false); } m_keep_alive = 0; diff --git a/src/items/plunger.hpp b/src/items/plunger.hpp index 951a1eef1..446c7c2ed 100644 --- a/src/items/plunger.hpp +++ b/src/items/plunger.hpp @@ -1,9 +1,9 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // Physics improvements and linear intersection algorithm by -// by David Mikos. Copyright (C) 2009. +// Copyright (C) 2009-2013 David Mikos. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -51,7 +51,6 @@ public: static void init(const XMLNode &node, scene::IMesh* missile); virtual bool updateAndDelete(float dt); virtual void hitTrack (); - virtual const core::stringw getHitString(const AbstractKart *kart) const; virtual bool hit (AbstractKart *kart, PhysicalObject *obj=NULL); // ------------------------------------------------------------------------ diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index c82f88c60..c86cc85cd 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -29,64 +29,11 @@ #include "karts/controller/controller.hpp" #include "karts/kart_properties.hpp" #include "modes/world.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done -const wchar_t* getAnchorString() -{ - const int ANCHOR_STRINGS_COUNT = 3; - - RandomGenerator r; - const int id = r.get(ANCHOR_STRINGS_COUNT); - - switch (id) - { - //I18N: shown when anchor applied. %s is the victim. - case 0: return _LTR("Arrr, the %s dropped anchor, Captain!"); - case 1: return _LTR("%s pays the next round of grog!"); - case 2: return _LTR("%s is a mighty pirate!"); - default: assert(false); return L""; // avoid compiler warning. - } -} // getAnchorString - -//----------------------------------------------------------------------------- -const wchar_t* getParachuteString() -{ - const int PARACHUTE_STRINGS_COUNT = 3; - - RandomGenerator r; - const int id = r.get(PARACHUTE_STRINGS_COUNT); - - switch (id) - { - case 0: return _("Geronimo!!!"); // Parachutist shout - case 1: return _("The Space Shuttle has landed!"); - case 2: return _("Do you want to fly kites?"); - default: assert(false); return L""; // avoid compiler warning - } -} // getParachuteString - -//----------------------------------------------------------------------------- -const wchar_t* getSwapperString() -{ - const int SWAPPER_STRINGS_COUNT = 3; - - RandomGenerator r; - const int id = r.get(SWAPPER_STRINGS_COUNT); - - switch (id) - { - case 0: return _("Magic, son. Nothing else in the world smells like that."); - case 1: return _("A wizard did it!"); - case 2: return _("Banana? Box? Banana? Box? Banana? Box?"); - default: assert(false); return L""; // avoid compiler warning - } -} // getSwapperString - //----------------------------------------------------------------------------- /** Constructor, stores the kart to which this powerup belongs. * \param kart The kart to which this powerup belongs. @@ -239,7 +186,6 @@ void Powerup::use() m_number--; World *world = World::getWorld(); - RaceGUIBase* gui = world->getRaceGUI(); switch (m_type) { case PowerupManager::POWERUP_ZIPPER: @@ -250,9 +196,6 @@ void Powerup::use() ItemManager::get()->switchItems(); m_sound_use->position(m_owner->getXYZ()); m_sound_use->play(); - - gui->addMessage(getSwapperString(), NULL, 3.0f, - video::SColor(255, 255, 255, 255), false); break; } case PowerupManager::POWERUP_CAKE: @@ -361,11 +304,6 @@ void Powerup::use() m_sound_use->position(m_owner->getXYZ()); m_sound_use->play(); - - irr::core::stringw anchor_message; - anchor_message += StringUtils::insertValues(getAnchorString(), core::stringw(kart->getName())); - gui->addMessage(translations->fribidize(anchor_message), NULL, 3.0f, - video::SColor(255, 255, 255, 255), false); break; } } @@ -407,9 +345,6 @@ void Powerup::use() else if(player_kart) m_sound_use->position(player_kart->getXYZ()); m_sound_use->play(); - - gui->addMessage(getParachuteString(), NULL, 3.0f, - video::SColor(255, 255, 255, 255), false); } break; @@ -458,13 +393,21 @@ void Powerup::hitBonusBox(const Item &item, int add_info) // Check if two bouncing balls are collected less than getRubberBallTimer() //seconds apart. If yes, then call getRandomPowerup again. If no, then break. - for(int i=0; i<20; i++) + if (add_info<0) { - new_powerup = powerup_manager->getRandomPowerup(position, &n); - if(new_powerup != PowerupManager::POWERUP_RUBBERBALL || - ( World::getWorld()->getTime() - powerup_manager->getBallCollectTime()) > - RubberBall::getTimeBetweenRubberBalls() ) - break; + for(int i=0; i<20; i++) + { + new_powerup = powerup_manager->getRandomPowerup(position, &n); + if(new_powerup != PowerupManager::POWERUP_RUBBERBALL || + ( World::getWorld()->getTime() - powerup_manager->getBallCollectTime()) > + RubberBall::getTimeBetweenRubberBalls() ) + break; + } + } + else // set powerup manually + { + new_powerup = (PowerupManager::PowerupType)((add_info>>4)&0x0f); // highest 4 bits for the type + n = (add_info&0x0f); // last 4 bits for the amount } if(new_powerup == PowerupManager::POWERUP_RUBBERBALL) diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index 4d76dad48..a21335342 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index 5d39c84df..3536a0ce4 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index 096b6a6a1..ec6ce3c7c 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/projectile_manager.cpp b/src/items/projectile_manager.cpp index ce96adb6e..091c64f97 100644 --- a/src/items/projectile_manager.cpp +++ b/src/items/projectile_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -26,8 +26,7 @@ #include "items/powerup_manager.hpp" #include "items/powerup.hpp" #include "items/rubber_ball.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" +#include "karts/abstract_kart.hpp" ProjectileManager *projectile_manager=0; @@ -66,14 +65,7 @@ void ProjectileManager::cleanup() /** General projectile update call. */ void ProjectileManager::update(float dt) { - if(network_manager->getMode()==NetworkManager::NW_CLIENT) - { - updateClient(dt); - } - else - { - updateServer(dt); - } + updateServer(dt); HitEffects::iterator he = m_active_hit_effects.begin(); while(he!=m_active_hit_effects.end()) @@ -100,23 +92,10 @@ void ProjectileManager::update(float dt) /** Updates all rockets on the server (or no networking). */ void ProjectileManager::updateServer(float dt) { - // First update all projectiles on the track - if(network_manager->getMode()!=NetworkManager::NW_NONE) - { - race_state->setNumFlyables(m_active_projectiles.size()); - } - Projectiles::iterator p = m_active_projectiles.begin(); while(p!=m_active_projectiles.end()) { bool can_be_deleted = (*p)->updateAndDelete(dt); - if(network_manager->getMode()!=NetworkManager::NW_NONE) - { - race_state->setFlyableInfo(p-m_active_projectiles.begin(), - FlyableInfo((*p)->getXYZ(), - (*p)->getRotation(), - can_be_deleted) ); - } if(can_be_deleted) { HitEffect *he = (*p)->getHitEffect(); @@ -130,33 +109,9 @@ void ProjectileManager::updateServer(float dt) else p++; } // while p!=m_active_projectiles.end() + } // updateServer -// ----------------------------------------------------------------------------- -/** Updates all rockets and hit effects on the client. - * updateClient takes the information in race_state and updates all rockets - * (i.e. position, hit effects etc) */ -void ProjectileManager::updateClient(float dt) -{ - unsigned int num_projectiles = race_state->getNumFlyables(); - if(num_projectiles != m_active_projectiles.size()) - fprintf(stderr, "Warning: num_projectiles %d active %d\n", - num_projectiles, (int)m_active_projectiles.size()); - - unsigned int indx=0; - for(Projectiles::iterator i = m_active_projectiles.begin(); - i != m_active_projectiles.end(); ++i, ++indx) - { - const FlyableInfo &f = race_state->getFlyable(indx); - (*i)->updateFromServer(f, dt); - if(f.m_exploded) - { - (*i)->hit(NULL); - } - } // for i in m_active_projectiles - -} // updateClient - // ----------------------------------------------------------------------------- /** Creates a new projectile of the given type. * \param kart The kart which shoots the projectile. @@ -178,6 +133,7 @@ Flyable *ProjectileManager::newProjectile(AbstractKart *kart, m_active_projectiles.push_back(f); return f; } // newProjectile + // ----------------------------------------------------------------------------- /** Returns true if a projectile is within the given distance of the specified * kart. @@ -196,4 +152,4 @@ bool ProjectileManager::projectileIsClose(const AbstractKart * const kart, if(dist2getXYZ()-getXYZ(); - *next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff; + // Avoid potential division by zero + if(diff.length2()==0) + *next_xyz = getXYZ(); + else + *next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff; Vec3 old_vec = getXYZ()-m_previous_xyz; Vec3 new_vec = *next_xyz - getXYZ(); @@ -574,7 +556,7 @@ float RubberBall::updateHeight() if(m_current_max_height>m_max_height) m_current_max_height = m_max_height; m_interval = m_current_max_height / 10.0f; - // Avoid too small hops and esp. a division by zero + // Avoid too small hops and esp. a division by zero if(m_interval<0.01f) m_interval = 0.1f; } diff --git a/src/items/rubber_ball.hpp b/src/items/rubber_ball.hpp index f0a1da0a9..deb0cc776 100644 --- a/src/items/rubber_ball.hpp +++ b/src/items/rubber_ball.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -207,8 +207,6 @@ public: static void init(const XMLNode &node, scene::IMesh *rubberball); virtual bool updateAndDelete(float dt); virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); - virtual const core::stringw getHitString(const AbstractKart *kart) const; - static float getTimeBetweenRubberBalls() {return m_time_between_balls;} // ------------------------------------------------------------------------ /** This object does not create an explosion, all affects on diff --git a/src/items/rubber_band.cpp b/src/items/rubber_band.cpp index 538708010..99ad6e16a 100644 --- a/src/items/rubber_band.cpp +++ b/src/items/rubber_band.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -67,7 +67,7 @@ const wchar_t* getPlungerString() RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart) : m_plunger(plunger), m_owner(kart) { - video::SColor color(77, 179, 0, 0); + const video::SColor color(77, 179, 0, 0); video::SMaterial m; m.AmbientColor = color; m.DiffuseColor = color; @@ -78,8 +78,19 @@ RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart) m_attached_state = RB_TO_PLUNGER; assert(m_buffer->getVertexType()==video::EVT_STANDARD); + // Set the vertex colors properly, as the new pipeline doesn't use the old light values + u32 i; + scene::IMeshBuffer * const mb = m_mesh->getMeshBuffer(0); + video::S3DVertex * const verts = (video::S3DVertex *) mb->getVertices(); + const u32 max = mb->getVertexCount(); + for (i = 0; i < max; i++) + { + verts[i].Color = color; + } + updatePosition(); m_node = irr_driver->addMesh(m_mesh); + irr_driver->applyObjectPassShader(m_node); #ifdef DEBUG std::string debug_name = m_owner->getIdent()+" (rubber-band)"; m_node->setName(debug_name.c_str()); diff --git a/src/items/rubber_band.hpp b/src/items/rubber_band.hpp index bab96d15d..68e07fb7e 100644 --- a/src/items/rubber_band.hpp +++ b/src/items/rubber_band.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/items/swatter.cpp b/src/items/swatter.cpp index 0a147dadb..65c3bbda1 100644 --- a/src/items/swatter.cpp +++ b/src/items/swatter.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -295,16 +295,6 @@ void Swatter::squashThingsAround() kart->setSquash(kp->getSquashDuration(), kp->getSquashSlowdown()); - RaceGUIBase* gui = World::getWorld()->getRaceGUI(); - irr::core::stringw hit_message = - StringUtils::insertValues(getHitString(kart), - core::stringw(kart->getName()), - core::stringw(m_kart->getName()) - ); - if(hit_message.size()>0) - gui->addMessage(translations->fribidize(hit_message), NULL, 3.0f, - video::SColor(255, 255, 255, 255), false); - if (kart->getAttachment()->getType()==Attachment::ATTACH_BOMB) { // make bomb explode kart->getAttachment()->update(10000); @@ -318,28 +308,4 @@ void Swatter::squashThingsAround() } // for i < num_kartrs // TODO: squash items -} - - - -// ---------------------------------------------------------------------------- -/** Picks a random message to be displayed when a kart is hit by a swatter - * \param kart The kart that was hit. - * \returns The string to display. - */ -const core::stringw Swatter::getHitString(const AbstractKart *kart) const -{ - RandomGenerator r; - - const int SWATTER_STRINGS_AMOUNT = 3; - switch (r.get(SWATTER_STRINGS_AMOUNT)) - { - //I18N: shown when hit by swatter. %1 is the attacker, %0 is the victim. - case 0 : return _LTR("%1 thinks %0 is a big fly"); - //I18N: shown when hit by swatter. %1 is the attacker, %0 is the victim. - case 1 : return _LTR("%1 flattens %0"); - //I18N: shown when hit by swatter. %s is the victim - case 2 : return _LTR("%s feels flat today"); - default: assert(false); return L""; // avoid compiler warning - } -} +} // squashThingsAround diff --git a/src/items/swatter.hpp b/src/items/swatter.hpp index 5d5c55a23..5dd42a8e6 100644 --- a/src/items/swatter.hpp +++ b/src/items/swatter.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -67,8 +67,6 @@ private: /** For some reason the built-in animation system doesn't work correctly here?? */ float m_swat_bomb_frame; - const core::stringw getHitString(const AbstractKart *kart) const; - public: Swatter(AbstractKart *kart, bool was_bomb, scene::ISceneNode* bomb_scene_node); diff --git a/src/karts/abstract_kart.cpp b/src/karts/abstract_kart.cpp index 9d0fe6ed0..8024f23f9 100644 --- a/src/karts/abstract_kart.cpp +++ b/src/karts/abstract_kart.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -53,10 +53,8 @@ AbstractKart::AbstractKart(const std::string& ident, m_kart_width = m_kart_model->getWidth(); m_kart_height = m_kart_model->getHeight(); m_kart_length = m_kart_model->getLength(); - m_kart_highest_point = m_kart_model->getHighestPoint(); + m_kart_highest_point = m_kart_model->getHighestPoint(); m_wheel_graphics_position = m_kart_model->getWheelsGraphicsPosition(); - m_nitro_emitter_position = m_kart_model->getNitroEmittersPositon(); - m_has_nitro_emitter = m_kart_model->hasNitroEmitters(); } // AbstractKart // ---------------------------------------------------------------------------- diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index a698b73bb..fee72965f 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -51,14 +51,10 @@ private: float m_kart_width; /** Height of the kart, copy of the data from KartModel. */ float m_kart_height; - /** Coordinate on up axis */ - float m_kart_highest_point; + /** Coordinate on up axis */ + float m_kart_highest_point; /** The position of all four wheels in the 3d model */ const Vec3* m_wheel_graphics_position; - /** The position of all nitro emitters in the 3d model */ - const Vec3* m_nitro_emitter_position; - /** True if kart has nitro emitters */ - bool m_has_nitro_emitter; /** Index of kart in world. */ unsigned int m_world_kart_id; @@ -144,6 +140,9 @@ public: /** Returns this kart's kart model. */ KartModel* getKartModel() { return m_kart_model; } // ------------------------------------------------------------------------ + /** Returns this kart's kart model. */ + const KartModel* getKartModel() const { return m_kart_model; } + // ------------------------------------------------------------------------ /** Returns the length of the kart. */ float getKartLength() const { return m_kart_length; } // ------------------------------------------------------------------------ @@ -152,9 +151,9 @@ public: // ------------------------------------------------------------------------ /** Returns the width of the kart. */ float getKartWidth() const {return m_kart_width; } - // ------------------------------------------------------------------------ - /** Returns the highest point of the kart (coordinate on up axis) */ - float getHighestPoint() const { return m_kart_highest_point; } + // ------------------------------------------------------------------------ + /** Returns the highest point of the kart (coordinate on up axis) */ + float getHighestPoint() const { return m_kart_highest_point; } // ------------------------------------------------------------------------ /** Returns true if this kart has no wheels. */ bool isWheeless() const; @@ -164,14 +163,6 @@ public: * right, 3 = rear left. */ const Vec3& getWheelGraphicsPosition(int i) const {assert(i>=0 && i<4); return m_wheel_graphics_position[i];} - // ------------------------------------------------------------------------ - /** Returns the position of a nitro emitter relative to the kart */ - const Vec3& getNitroEmitterPosition(int i) const - {assert(i>=0 && i<2); return m_nitro_emitter_position[i];} - // ------------------------------------------------------------------------ - /** Returns true if kart has nitro emitters */ - const bool hasNitroEmitter() const - {return m_has_nitro_emitter;} // ======================================================================== // Emergency animation related functions. @@ -281,7 +272,7 @@ public: * \param add_info Additional info, used in networking games to force * a specific item to be used (instead of a random item) to keep * all karts in synch. */ - virtual void collectedItem(Item *item, int random_attachment) = 0; + virtual void collectedItem(Item *item, int add_info) = 0; // ------------------------------------------------------------------------ /** Returns the current position of this kart in the race. */ virtual int getPosition() const = 0; @@ -363,14 +354,6 @@ public: * over. */ virtual void startEngineSFX() = 0; // ------------------------------------------------------------------------ - /** Stores the current suspension length. This function is called from - * world after all karts are in resting position (see - * World::resetAllKarts), so that the default suspension rest length can - * be stored. This is then used later to move the wheels depending on - * actual suspension, so that when a kart is in rest, the wheels are at - * the position at which they were modelled. */ - virtual void setSuspensionLength() = 0; - // ------------------------------------------------------------------------ /** This method is to be called every time the mass of the kart is updated, * which includes attaching an anvil to the kart (and detaching). */ virtual void updateWeight() = 0; diff --git a/src/karts/abstract_kart_animation.cpp b/src/karts/abstract_kart_animation.cpp index 3c85fbb07..7062c3f7a 100644 --- a/src/karts/abstract_kart_animation.cpp +++ b/src/karts/abstract_kart_animation.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/abstract_kart_animation.hpp b/src/karts/abstract_kart_animation.hpp index 423b69b15..14f318e82 100644 --- a/src/karts/abstract_kart_animation.hpp +++ b/src/karts/abstract_kart_animation.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -41,8 +41,8 @@ private: /** Name of this animation, used for debug prints only. */ std::string m_name; protected: - /** A pointer to the kart which is animated by this class. */ - AbstractKart *m_kart; + /** A pointer to the kart which is animated by this class. */ + AbstractKart *m_kart; /** Timer for the explosion. */ float m_timer; diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 8d98c40f8..cc47cba94 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index 39057c37f..7a58cc414 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/controller/ai_properties.cpp b/src/karts/controller/ai_properties.cpp index f5bc0b99f..c9dc6f655 100644 --- a/src/karts/controller/ai_properties.cpp +++ b/src/karts/controller/ai_properties.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/controller/ai_properties.hpp b/src/karts/controller/ai_properties.hpp index 45a474f96..de2d160c5 100644 --- a/src/karts/controller/ai_properties.hpp +++ b/src/karts/controller/ai_properties.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 3ef553d1d..f3d784a3e 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -122,8 +122,10 @@ void BattleAI::handleGetUnstuck(const float dt) } if(m_currently_reversing == true) { - m_controls->m_accel = -0.30f; - setSteering(-1.0f*m_target_angle,dt); + m_controls->m_accel = -0.34f; + if(m_target_angle > 0) + setSteering(M_PI,dt); + else setSteering(-M_PI,dt); m_time_since_stuck += dt; if(m_time_since_stuck >= 0.6f) @@ -136,7 +138,7 @@ void BattleAI::handleGetUnstuck(const float dt) -// Handles steering. Returns the steering angle being used for setSteering() +// Handles steering. void BattleAI::handleSteering(const float dt) { Vec3 target_point; diff --git a/src/karts/controller/controller.cpp b/src/karts/controller/controller.cpp index 0677ecffe..334e2bf08 100644 --- a/src/karts/controller/controller.cpp +++ b/src/karts/controller/controller.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index a981176d2..556d3b699 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -37,7 +37,7 @@ class Item; class KartControl; class Material; -/** This is the base class for kart controller - that can be a player +/** This is the base class for kart controller - that can be a player * or a a robot. * \ingroup controller */ @@ -58,7 +58,7 @@ protected: /** The name of the controller, mainly used for debugging purposes. */ std::string m_controller_name; public: - Controller (AbstractKart *kart, + Controller (AbstractKart *kart, StateManager::ActivePlayer *player=NULL); virtual ~Controller () {}; virtual void reset () = 0; @@ -72,22 +72,22 @@ public: virtual bool isPlayerController () const = 0; virtual bool isNetworkController() const = 0; virtual bool disableSlipstreamBonus() const = 0; - // --------------------------------------------------------------------------- + // --------------------------------------------------------------------------- /** Sets the controller name for this controller. */ - virtual void setControllerName(const std::string &name) + virtual void setControllerName(const std::string &name) { m_controller_name = name; } - // --------------------------------------------------------------------------- + // --------------------------------------------------------------------------- /** Returns the name of this controller. */ const std::string &getControllerName() const { return m_controller_name; } - // --------------------------------------------------------------------------- - /** Returns the active player for this controller (NULL + // --------------------------------------------------------------------------- + /** Returns the active player for this controller (NULL * if this controller does not belong to a player. */ StateManager::ActivePlayer *getPlayer () {return m_player;} - - // --------------------------------------------------------------------------- - /** Returns the player object (or NULL if it's a computer controller). */ + + // --------------------------------------------------------------------------- + /** Returns the player object (or NULL if it's a computer controller). */ const StateManager::ActivePlayer *getPlayer () const { return m_player; } - + // ------------------------------------------------------------------------ /** Default: ignore actions. Only PlayerController get them. */ virtual void action(PlayerAction action, int value) = 0; @@ -101,6 +101,9 @@ public: /** Called whan this controller's kart finishes the last lap. */ virtual void finishedRace(float time) = 0; // ------------------------------------------------------------------------ + /** Get a pointer on the kart controls. */ + virtual KartControl* getControls() { return m_controls; } + // ------------------------------------------------------------------------ }; // Controller #endif diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index 15264c344..17c9ebedf 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -1,8 +1,8 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2010 Steve Baker -// Copyright (C) 2006-2010 Eduardo Hernandez Munoz -// Copyright (C) 2008-2010 Joerg Henrichs +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Eduardo Hernandez Munoz +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -43,7 +43,6 @@ #include "karts/max_speed.hpp" #include "karts/rescue_animation.hpp" #include "modes/linear_world.hpp" -#include "network/network_manager.hpp" #include "race/race_manager.hpp" #include "states_screens/race_result_gui.hpp" #include "tracks/quad_graph.hpp" @@ -184,7 +183,8 @@ void EndController::update(float dt) // In case of battle mode: don't do anything if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES || - race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER) + race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER || + race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG) { m_controls->m_accel = 0.0f; // Brake while we are still driving forwards (if we keep diff --git a/src/karts/controller/end_controller.hpp b/src/karts/controller/end_controller.hpp index bbfb8c56c..63d7c136e 100644 --- a/src/karts/controller/end_controller.hpp +++ b/src/karts/controller/end_controller.hpp @@ -1,8 +1,8 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2010 Steve Baker -// Copyright (C) 2006-2010 Eduardo Hernandez Munoz -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Eduardo Hernandez Munoz +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index 78f789e7c..68cc312ff 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,9 +19,8 @@ #ifndef HEADER_KART_CONTROL_HPP #define HEADER_KART_CONTROL_HPP -#include "network/message.hpp" -/** +/** * \ingroup controller */ class KartControl @@ -52,15 +51,6 @@ public: reset(); } // ------------------------------------------------------------------------ - /** Construct kart control from a Message (i.e. unserialise) */ - KartControl(Message *m) - { - m_steer = m->getFloat(); - m_accel = m->getFloat(); - char c = m->getChar(); - setButtonsCompressed(c); - } // KartControl(Message*) - // ------------------------------------------------------------------------ /** Resets all controls. */ void reset() { @@ -74,17 +64,6 @@ public: m_look_back = false; } // reset // ------------------------------------------------------------------------ - /** Return the serialised size in bytes. */ - static int getLength() { return 9; } - // ------------------------------------------------------------------------ - /** Serialises the kart control into a message. */ - void serialise(Message *m) const - { - m->addFloat(m_steer); - m->addFloat(m_accel); - m->addChar(getButtonsCompressed()); - } // compress - // ------------------------------------------------------------------------ void uncompress(char *c) { m_steer = ((float*)c)[0]; diff --git a/src/karts/controller/network_player_controller.cpp b/src/karts/controller/network_player_controller.cpp new file mode 100644 index 000000000..b408727ec --- /dev/null +++ b/src/karts/controller/network_player_controller.cpp @@ -0,0 +1,368 @@ +#include "karts/controller/network_player_controller.hpp" + +#include "config/player.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/post_processing.hpp" +#include "input/input_manager.hpp" +#include "items/attachment.hpp" +#include "items/item.hpp" +#include "items/powerup.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/kart_properties.hpp" +#include "karts/skidding.hpp" +#include "karts/rescue_animation.hpp" +#include "modes/world.hpp" +#include "race/history.hpp" +#include "states_screens/race_gui_base.hpp" +#include "utils/constants.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" + +NetworkPlayerController::NetworkPlayerController(AbstractKart *kart, + StateManager::ActivePlayer *player) + : Controller(kart) +{ + assert(player != NULL); + m_player = player; + m_player->setKart(kart); + m_penalty_time = 0.0f; + + reset(); + + Log::info("NetworkPlayerController", "New network player controller."); +} // NetworkPlayerController + +//----------------------------------------------------------------------------- +/** Destructor for a player kart. + */ +NetworkPlayerController::~NetworkPlayerController() +{ +} // ~NetworkPlayerController + +//----------------------------------------------------------------------------- +/** Resets the player kart for a new or restarted race. + */ +void NetworkPlayerController::reset() +{ + m_steer_val_l = 0; + m_steer_val_r = 0; + m_steer_val = 0; + m_prev_brake = 0; + m_prev_accel = 0; + m_prev_nitro = false; + m_penalty_time = 0; +} // reset + +// ---------------------------------------------------------------------------- +/** Resets the state of control keys. This is used after the in-game menu to + * avoid that any keys pressed at the time the menu is opened are still + * considered to be pressed. + */ +void NetworkPlayerController::resetInputState() +{ + m_steer_val_l = 0; + m_steer_val_r = 0; + m_steer_val = 0; + m_prev_brake = 0; + m_prev_accel = 0; + m_prev_nitro = false; + m_controls->reset(); +} // resetInputState + +// ---------------------------------------------------------------------------- +/** This function interprets a kart action and value, and set the corresponding + * entries in the kart control data structure. This function handles esp. + * cases like 'press left, press right, release right' - in this case after + * releasing right, the steering must switch to left again. Similarly it + * handles 'press left, press right, release left' (in which case still + * right must be selected). Similarly for braking and acceleration. + * \param action The action to be executed. + * \param value If 32768, it indicates a digital value of 'fully set' + * if between 1 and 32767, it indicates an analog value, + * and if it's 0 it indicates that the corresponding button + * was released. + */ +void NetworkPlayerController::action(PlayerAction action, int value) +{ + switch (action) + { + case PA_STEER_LEFT: + m_steer_val_l = value; + if (value) + { + m_steer_val = value; + if(m_controls->m_skid==KartControl::SC_NO_DIRECTION) + m_controls->m_skid = KartControl::SC_LEFT; + } + else + m_steer_val = m_steer_val_r; + + break; + case PA_STEER_RIGHT: + m_steer_val_r = -value; + if (value) + { + m_steer_val = -value; + if(m_controls->m_skid==KartControl::SC_NO_DIRECTION) + m_controls->m_skid = KartControl::SC_RIGHT; + } + else + m_steer_val = m_steer_val_l; + + break; + case PA_ACCEL: + m_prev_accel = value; + if (value && !(m_penalty_time > 0.0f)) + { + m_controls->m_accel = value/32768.0f; + m_controls->m_brake = false; + m_controls->m_nitro = m_prev_nitro; + } + else + { + m_controls->m_accel = 0.0f; + m_controls->m_brake = m_prev_brake; + m_controls->m_nitro = false; + } + break; + case PA_BRAKE: + m_prev_brake = value!=0; + // let's consider below that to be a deadzone + if(value > 32768/2) + { + m_controls->m_brake = true; + m_controls->m_accel = 0.0f; + m_controls->m_nitro = false; + } + else + { + m_controls->m_brake = false; + m_controls->m_accel = m_prev_accel/32768.0f; + // Nitro still depends on whether we're accelerating + m_controls->m_nitro = (m_prev_nitro && m_prev_accel); + } + break; + case PA_NITRO: + // This basically keeps track whether the button still is being pressed + m_prev_nitro = (value != 0); + // Enable nitro only when also accelerating + m_controls->m_nitro = ((value!=0) && m_controls->m_accel); + break; + case PA_RESCUE: + m_controls->m_rescue = (value!=0); + break; + case PA_FIRE: + { + m_controls->m_fire = (value!=0); + break; + } + case PA_LOOK_BACK: + m_controls->m_look_back = (value!=0); + break; + case PA_DRIFT: + if(value==0) + m_controls->m_skid = KartControl::SC_NONE; + else + if(m_steer_val==0) + m_controls->m_skid = KartControl::SC_NO_DIRECTION; + else + m_controls->m_skid = m_steer_val<0 + ? KartControl::SC_RIGHT + : KartControl::SC_LEFT; + break; + case PA_PAUSE_RACE: + if (value != 0) StateManager::get()->escapePressed(); + break; + default: + break; + } + +} // action + +//----------------------------------------------------------------------------- +/** Handles steering for a player kart. + */ +void NetworkPlayerController::steer(float dt, int steer_val) +{ + if(stk_config->m_disable_steer_while_unskid && + m_controls->m_skid==KartControl::SC_NONE && + m_kart->getSkidding()->getVisualSkidRotation()!=0) + { + m_controls->m_steer = 0; + } + + + // Amount the steering is changed for digital devices. + // If the steering is 'back to straight', a different steering + // change speed is used. + const float STEER_CHANGE = ( (steer_val<=0 && m_controls->m_steer<0) || + (steer_val>=0 && m_controls->m_steer>0) ) + ? dt/m_kart->getKartProperties()->getTimeResetSteer() + : dt/m_kart->getTimeFullSteer(fabsf(m_controls->m_steer)); + if (steer_val < 0) + { + // If we got analog values do not cumulate. + if (steer_val > -32767) + m_controls->m_steer = -steer_val/32767.0f; + else + m_controls->m_steer += STEER_CHANGE; + } + else if(steer_val > 0) + { + // If we got analog values do not cumulate. + if (steer_val < 32767) + m_controls->m_steer = -steer_val/32767.0f; + else + m_controls->m_steer -= STEER_CHANGE; + } + else + { // no key is pressed + if(m_controls->m_steer>0.0f) + { + m_controls->m_steer -= STEER_CHANGE; + if(m_controls->m_steer<0.0f) m_controls->m_steer=0.0f; + } + else + { // m_controls->m_steer<=0.0f; + m_controls->m_steer += STEER_CHANGE; + if(m_controls->m_steer>0.0f) m_controls->m_steer=0.0f; + } // if m_controls->m_steer<=0.0f + } // no key is pressed + if(UserConfigParams::m_gamepad_debug) + { + Log::debug("PlayerController", " set to: %f\n", m_controls->m_steer); + } + + m_controls->m_steer = std::min(1.0f, std::max(-1.0f, m_controls->m_steer)); + +} // steer + +//----------------------------------------------------------------------------- +/** Callback when the skidding bonus is triggered. The player controller + * resets the current steering to 0, which makes the kart easier to control. + */ +void NetworkPlayerController::skidBonusTriggered() +{ + m_controls->m_steer = 0; +} // skidBonusTriggered + +//----------------------------------------------------------------------------- +/** Updates the player kart, called once each timestep. + */ +void NetworkPlayerController::update(float dt) +{ + if (UserConfigParams::m_gamepad_debug) + { + // Print a dividing line so that it's easier to see which events + // get received in which order in the one frame. + Log::debug("PlayerController", "irr_driver", "-------------------------------------\n"); + } + + // Don't do steering if it's replay. In position only replay it doesn't + // matter, but if it's physics replay the gradual steering causes + // incorrect results, since the stored values are already adjusted. + if (!history->replayHistory()) + steer(dt, m_steer_val); + + if (World::getWorld()->isStartPhase()) + { + if (m_controls->m_accel || m_controls->m_brake || + m_controls->m_fire || m_controls->m_nitro) + { + // Only give penalty time in SET_PHASE. + // Penalty time check makes sure it doesn't get rendered on every + // update. + if (m_penalty_time == 0.0 && + World::getWorld()->getPhase() == WorldStatus::SET_PHASE) + { + RaceGUIBase* m=World::getWorld()->getRaceGUI(); + if (m) + { + m->addMessage(_("Penalty time!!"), m_kart, 2.0f, + video::SColor(255, 255, 128, 0)); + m->addMessage(_("Don't accelerate before go"), m_kart, 2.0f, + video::SColor(255, 210, 100, 50)); + } + + m_penalty_time = stk_config->m_penalty_time; + } // if penalty_time = 0 + + m_controls->m_brake = false; + m_controls->m_accel = 0.0f; + } // if key pressed + + return; + } // if isStartPhase + + if (m_penalty_time>0.0) + { + m_penalty_time-=dt; + return; + } + + // We can't restrict rescue to fulfil isOnGround() (which would be more like + // MK), since e.g. in the City track it is possible for the kart to end + // up sitting on a brick wall, with all wheels in the air :(( + // Only accept rescue if there is no kart animation is already playing + // (e.g. if an explosion happens, wait till the explosion is over before + // starting any other animation). + if ( m_controls->m_rescue && !m_kart->getKartAnimation() ) + { + new RescueAnimation(m_kart); + m_controls->m_rescue=false; + } +} // update + +//----------------------------------------------------------------------------- +/** Checks if the kart was overtaken, and if so plays a sound +*/ +void NetworkPlayerController::setPosition(int p) +{ + if(m_kart->getPosition()getNumKarts(); i++ ) + { + AbstractKart *kart = world->getKart(i); + if(kart->getPosition() == p + 1) + { + kart->beep(); + break; + } + } + } +} // setPosition + +//----------------------------------------------------------------------------- +/** Called when a kart finishes race. + * /param time Finishing time for this kart. + d*/ +void NetworkPlayerController::finishedRace(float time) +{ + +} // finishedRace + +//----------------------------------------------------------------------------- +/** Called when a kart hits or uses a zipper. + */ +void NetworkPlayerController::handleZipper(bool play_sound) +{ + m_kart->showZipperFire(); +} // handleZipper + +//----------------------------------------------------------------------------- +/** Called when a kart hits an item. + * \param item Item that was collected. + * \param add_info Additional info to be used then handling the item. If + * this is -1 (default), the item type is selected + * randomly. Otherwise it contains the powerup or + * attachment for the kart. This is used in network mode to + * let the server determine the powerup/attachment for + * the clients. + */ +void NetworkPlayerController::collectedItem(const Item &item, int add_info, float old_energy) +{ + +} // collectedItem diff --git a/src/karts/controller/network_player_controller.hpp b/src/karts/controller/network_player_controller.hpp new file mode 100644 index 000000000..fba1f70ef --- /dev/null +++ b/src/karts/controller/network_player_controller.hpp @@ -0,0 +1,48 @@ +#ifndef NETWORK_PLAYER_CONTROLLER_HPP +#define NETWORK_PLAYER_CONTROLLER_HPP + +#include "karts/controller/controller.hpp" + +class AbstractKart; +class Player; + +class NetworkPlayerController : public Controller +{ +protected: + int m_steer_val, m_steer_val_l, m_steer_val_r; + int m_prev_accel; + bool m_prev_brake; + bool m_prev_nitro; + + float m_penalty_time; + + void steer(float, int); +public: + NetworkPlayerController (AbstractKart *kart, + StateManager::ActivePlayer *_player); + virtual ~NetworkPlayerController (); + void update (float); + void action (PlayerAction action, int value); + void handleZipper (bool play_sound); + void collectedItem (const Item &item, int add_info=-1, + float previous_energy=0); + virtual void skidBonusTriggered(); + virtual void setPosition (int p); + virtual bool isPlayerController() const { return false; } + virtual bool isNetworkController() const { return true; } + virtual void reset (); + void resetInputState (); + virtual void finishedRace (float time); + virtual void crashed (const AbstractKart *k) {} + virtual void crashed (const Material *m) {} + // ------------------------------------------------------------------------ + /** Callback whenever a new lap is triggered. Used by the AI + * to trigger a recomputation of the way to use, not used for players. */ + virtual void newLap(int lap) {} + // ------------------------------------------------------------------------ + /** Player will always be able to get a slipstream bonus. */ + virtual bool disableSlipstreamBonus() const { return false; } + +}; + +#endif // NETWORK_PLAYER_CONTROLLER_HPP diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index d7124d6a0..25294a8e2 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006-2009 Joerg Henrichs, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -35,6 +35,7 @@ #include "karts/skidding.hpp" #include "karts/rescue_animation.hpp" #include "modes/world.hpp" +#include "network/network_world.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/battle_graph.hpp" @@ -220,6 +221,10 @@ void PlayerController::action(PlayerAction action, int value) default: break; } + if (NetworkWorld::getInstance()->isRunning()) + { + NetworkWorld::getInstance()->controllerAction(this, action, value); + } } // action diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index 74cefbe8a..ddcb12cae 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 55c5a476c..190614af8 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -298,13 +298,6 @@ void SkiddingAI::update(float dt) return; #endif - // The client does not do any AI computations. - if(network_manager->getMode()==NetworkManager::NW_CLIENT) - { - AIBaseLapController::update(dt); - return; - } - // If the kart needs to be rescued, do it now (and nothing else) if(isStuck() && !m_kart->getKartAnimation()) { diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index 155ff0d61..aff93cb75 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -1,9 +1,9 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006-2007 Eduardo Hernandez Munoz -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Eduardo Hernandez Munoz +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/explosion_animation.cpp b/src/karts/explosion_animation.cpp index b929e8e51..08ac464ce 100644 --- a/src/karts/explosion_animation.cpp +++ b/src/karts/explosion_animation.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/explosion_animation.hpp b/src/karts/explosion_animation.hpp index 387dd6b58..457f6dcda 100644 --- a/src/karts/explosion_animation.hpp +++ b/src/karts/explosion_animation.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/ghost_kart.cpp b/src/karts/ghost_kart.cpp index caef906ed..36b49721e 100644 --- a/src/karts/ghost_kart.cpp +++ b/src/karts/ghost_kart.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/ghost_kart.hpp b/src/karts/ghost_kart.hpp index 3b691127e..9fb66dbea 100644 --- a/src/karts/ghost_kart.hpp +++ b/src/karts/ghost_kart.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ce3abf55e..fcd98b733 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 SuperTuxKart-Team, Joerg Henrichs, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 SuperTuxKart-Team, Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -59,7 +59,7 @@ #include "karts/max_speed.hpp" #include "karts/skidding.hpp" #include "modes/linear_world.hpp" -#include "network/race_state.hpp" +#include "network/network_world.hpp" #include "network/network_manager.hpp" #include "physics/btKart.hpp" #include "physics/btKartRaycast.hpp" @@ -70,6 +70,7 @@ #include "tracks/track_manager.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done +#include "utils/vs.hpp" @@ -801,29 +802,32 @@ void Kart::finishedRace(float time) // in modes that support it, start end animation setController(new EndController(this, m_controller->getPlayer(), m_controller)); - GameSlot *slot = unlock_manager->getCurrentSlot(); - const Challenge *challenge = slot->getCurrentChallenge(); - // In case of a GP challenge don't make the end animation depend - // on if the challenge is fulfilled - if(challenge && !challenge->getData()->isGrandPrix()) + if (m_controller->isPlayerController()) // if player is on this computer { - if(challenge->getData()->isChallengeFulfilled()) - m_kart_model->setAnimation(KartModel::AF_WIN_START); + GameSlot *slot = unlock_manager->getCurrentSlot(); + const Challenge *challenge = slot->getCurrentChallenge(); + // In case of a GP challenge don't make the end animation depend + // on if the challenge is fulfilled + if(challenge && !challenge->getData()->isGrandPrix()) + { + if(challenge->getData()->isChallengeFulfilled()) + m_kart_model->setAnimation(KartModel::AF_WIN_START); + else + m_kart_model->setAnimation(KartModel::AF_LOSE_START); + + } + else if(m_race_position<=0.5f*race_manager->getNumberOfKarts() || + m_race_position==1) + m_kart_model->setAnimation(KartModel::AF_WIN_START); else m_kart_model->setAnimation(KartModel::AF_LOSE_START); - } - else if(m_race_position<=0.5f*race_manager->getNumberOfKarts() || - m_race_position==1) - m_kart_model->setAnimation(KartModel::AF_WIN_START); - else - m_kart_model->setAnimation(KartModel::AF_LOSE_START); - - RaceGUIBase* m = World::getWorld()->getRaceGUI(); - if(m) - { - m->addMessage((getPosition() == 1 ? _("You won the race!") : _("You finished the race!")) , - this, 2.0f); + RaceGUIBase* m = World::getWorld()->getRaceGUI(); + if(m) + { + m->addMessage((getPosition() == 1 ? _("You won the race!") : _("You finished the race!")) , + this, 2.0f); + } } } else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) @@ -849,6 +853,12 @@ void Kart::finishedRace(float time) setController(new EndController(this, m_controller->getPlayer(), m_controller)); } + else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG) + { + m_kart_model->setAnimation(KartModel::AF_WIN_START); + setController(new EndController(this, m_controller->getPlayer(), + m_controller)); + } } // finishedRace @@ -902,15 +912,6 @@ void Kart::collectedItem(Item *item, int add_info) default : break; } // switch TYPE - // Attachments and powerups are stored in the corresponding - // functions (hit{Red,Green}Item), so only coins need to be - // stored here. - if(network_manager->getMode()==NetworkManager::NW_SERVER && - (type==Item::ITEM_NITRO_BIG || type==Item::ITEM_NITRO_SMALL) ) - { - race_state->itemCollected(getWorldKartId(), item->getItemId()); - } - if ( m_collected_energy > m_kart_properties->getNitroMax()) m_collected_energy = m_kart_properties->getNitroMax(); m_controller->collectedItem(*item, add_info, old_energy); @@ -1099,14 +1100,6 @@ void Kart::update(float dt) m_slipstream->update(dt); - // Store the actual kart controls at the start of update in the server - // state. This makes it easier to reset some fields when they are not used - // anymore (e.g. controls.fire). - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - race_state->storeKartControls(*this); - } - if (!m_flying) { // When really on air, free fly, when near ground, try to glide / adjust for landing @@ -1254,7 +1247,9 @@ void Kart::update(float dt) } // if there is material // Check if any item was hit. - ItemManager::get()->checkItemHit(this); + // check it if we're not in a network world, or if we're on the server (when network mode is on) + if (!NetworkWorld::getInstance()->isRunning() || NetworkManager::getInstance()->isServer()) + ItemManager::get()->checkItemHit(this); static video::SColor pink(255, 255, 133, 253); static video::SColor green(255, 61, 87, 23); @@ -1300,7 +1295,7 @@ void Kart::update(float dt) // take the same time again to reach the bottom float t = 2.0f * v/force; - // Jump if either the jump is estimated to be long enough, or + // Jump if either the jump is estimated to be long enough, or // the texture has the jump property set. if(t>getKartProperties()->getJumpAnimationTime() || last_m->isJumpTexture() ) @@ -1319,8 +1314,13 @@ void Kart::update(float dt) m_kart_model->setAnimation(KartModel::AF_DEFAULT); m_jump_time = 0; } - - if( (!isOnGround() || emergency) && m_shadow_enabled) + + //const bool dyn_shadows = World::getWorld()->getTrack()->hasShadows() && + // UserConfigParams::m_shadows && + // irr_driver->isGLSL(); + + // Disable the fake shadow if we're flying + if((!isOnGround() || emergency) && m_shadow_enabled) { m_shadow_enabled = false; m_shadow->disableShadow(); @@ -1969,7 +1969,7 @@ void Kart::updatePhysics(float dt) m_skidding->update(dt, isOnGround(), m_controls.m_steer, m_controls.m_skid); - + m_vehicle->setVisualRotation(m_skidding->getVisualSkidRotation()); if(( m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_LEFT || m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_RIGHT ) && m_skidding->getGraphicalJumpOffset()==0) @@ -1988,10 +1988,8 @@ void Kart::updatePhysics(float dt) updateSliding(); - // Only compute the current speed if this is not the client. On a client the - // speed is actually received from the server. - if(network_manager->getMode()!=NetworkManager::NW_CLIENT) - m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); + // Compute the speed of the kart. + m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); // calculate direction of m_speed const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); @@ -2027,6 +2025,13 @@ void Kart::updatePhysics(float dt) //at low velocity, forces on kart push it back and forth so we ignore this if(fabsf(m_speed) < 0.2f) // quick'n'dirty workaround for bug 1776883 m_speed = 0; + + if (dynamic_cast(getKartAnimation()) || + dynamic_cast(getKartAnimation())) + { + m_speed = 0; + } + updateEngineSFX(); #ifdef XX Log::info("Kart","forward %f %f %f %f side %f %f %f %f angVel %f %f %f heading %f\n" @@ -2334,23 +2339,6 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model) } // loadData // ---------------------------------------------------------------------------- -/** Stores the current suspension length. This function is called from world - * after all karts are in resting position (see World::resetAllKarts), so - * that the default suspension rest length can be stored. This is then used - * later to move the wheels depending on actual suspension, so that when - * a kart is in rest, the wheels are at the position at which they were - * modelled. - */ -void Kart::setSuspensionLength() -{ - for(unsigned int i=0; i<4; i++) - { - m_default_suspension_length[i] = - m_vehicle->getWheelInfo(i).m_raycastInfo.m_suspensionLength; - } // for i -} // setSuspensionLength - -//----------------------------------------------------------------------------- /** Applies engine power to all the wheels that are traction capable, * so other parts of code do not have to be adjusted to simulate different * kinds of vehicles in the general case, only if they are trying to @@ -2388,25 +2376,11 @@ void Kart::applyEngineForce(float force) void Kart::updateGraphics(float dt, const Vec3& offset_xyz, const btQuaternion& rotation) { - float wheel_up_axis[4]; - for(unsigned int i=0; i<4; i++) - { - // Set the suspension length - wheel_up_axis[i] = m_default_suspension_length[i] - - m_vehicle->getWheelInfo(i).m_raycastInfo.m_suspensionLength; - } - m_kart_model->update(m_wheel_rotation_dt, getSteerPercent(), wheel_up_axis, m_speed); - - Vec3 center_shift = m_kart_properties->getGravityCenterShift(); - float y = m_vehicle->getWheelInfo(0).m_chassisConnectionPointCS.getY() - - m_default_suspension_length[0] - - m_vehicle->getWheelInfo(0).m_wheelsRadius - - (m_kart_model->getWheelGraphicsRadius(0) - -m_kart_model->getWheelGraphicsPosition(0).getY() ); - y += m_skidding->getGraphicalJumpOffset(); - center_shift.setY(y); - - if ((m_controls.m_nitro || m_min_nitro_time > 0.0f) && isOnGround() && m_collected_energy > 0) + // Upate particle effects (creation rate, and emitter size + // depending on speed) + // -------------------------------------------------------- + if ( (m_controls.m_nitro || m_min_nitro_time > 0.0f) && + isOnGround() && m_collected_energy > 0 ) { // fabs(speed) is important, otherwise the negative number will // become a huge unsigned number in the particle scene node! @@ -2434,6 +2408,8 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, m_kart_gfx->resizeBox(KartGFX::KGFX_ZIPPER, getSpeed(), dt); + // Handle leaning of karts + // ----------------------- // Note that we compare with maximum speed of the kart, not // maximum speed including terrain effects. This avoids that // leaning might get less if a kart gets a special that increases @@ -2488,6 +2464,50 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, } } + // Now determine graphical chassis and wheel position depending on + // the physics result. The center of gravity of the chassis is at the + // bottom of the chassis, but the position of the graphical chassis is at + // the bottom of the wheels (i.e. in blender the kart is positioned on + // the horizonal plane through (0,0,0)). So first determine how far + // above the terrain is the center of the physics body. If the minimum + // of those values is larger than the lowest point of the chassis model + // the kart chassis would be too high (and look odd), so in this case + // move the chassis down so that the wheels (when touching the ground) + // look close enough to the chassis. + float height_above_terrain[4]; + float min_hat = 9999.9f; + for(unsigned int i=0; i<4; i++) + { + // Set the suspension length + height_above_terrain[i] = + ( m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS + - m_vehicle->getWheelInfo(i).m_raycastInfo.m_contactPointWS).length() + - m_vehicle->getWheelInfo(i).m_chassisConnectionPointCS.getY(); + if(height_above_terrain[i] < min_hat) min_hat = height_above_terrain[i]; + } + + float chassis_delta = 0; + // Check if the chassis needs to be moved down so that the wheels look + // like they are in the rest state, i.e. the wheels are not too far down. + if(min_hat > m_kart_model->getLowestPoint()) + { + chassis_delta = min_hat - m_kart_model->getLowestPoint(); + for(unsigned int i=0; i<4; i++) + height_above_terrain[i] -= chassis_delta; + } + + m_kart_model->update(dt, m_wheel_rotation_dt, getSteerPercent(), + height_above_terrain, m_speed); + + + // If the kart is leaning, part of the kart might end up 'in' the track. + // To avoid this, raise the kart enough to offset the leaning. + float lean_height = tan(fabsf(m_current_lean)) * getKartWidth()*0.5f; + + Vec3 center_shift = m_kart_properties->getGravityCenterShift(); + + center_shift.setY(m_skidding->getGraphicalJumpOffset() + lean_height + - m_kart_model->getLowestPoint() -chassis_delta ); float heading = m_skidding->getVisualSkidRotation(); Moveable::updateGraphics(dt, center_shift, btQuaternion(heading, 0, m_current_lean)); diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 1cb11fdc9..5dd049226 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 SuperTuxKart-Team, Joerg Henrichs, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 SuperTuxKart-Team, Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -184,12 +184,6 @@ private: /** Rotation change in the last time delta, same for all wheels */ float m_wheel_rotation_dt; - /** For each wheel it stores the suspension length after the karts are at - * the start position, i.e. the suspension will be somewhat compressed. - * The bullet suspensionRestLength is the value when the suspension is not - * at all compressed. */ - float m_default_suspension_length[4]; - /** The skidmarks object for this kart. */ SkidMarks *m_skidmarks; @@ -239,7 +233,6 @@ public: virtual void createPhysics (); virtual void updateWeight (); virtual bool isInRest () const; - virtual void setSuspensionLength(); virtual void applyEngineForce (float force); virtual void flyUp(); diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 36e479665..fa04661f9 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -40,20 +40,11 @@ KartGFX::KartGFX(const AbstractKart *kart) } m_kart = kart; - /* - // FIXME Old Positioning system Maybe usefull for a future effect - Vec3 rear_left(-(kart->getKartWidth()+0.20f)*0.35f, (kart->getKartHeight()-0.6f)*0.35f, - -kart->getKartLength()*0.35f); - Vec3 rear_right((kart->getKartWidth()+0.20f)*0.35f, (kart->getKartHeight()-0.6f)*0.35f, - -kart->getKartLength()*0.35f); - - - */ - Vec3 rear_left(kart->getWheelGraphicsPosition(3).toIrrVector().X, 0.05f, - kart->getWheelGraphicsPosition(3).toIrrVector().Z-0.1f); - Vec3 rear_right(kart->getWheelGraphicsPosition(2).toIrrVector().X, 0.05f, - kart->getWheelGraphicsPosition(2).toIrrVector().Z-0.1f); + Vec3 rear_left(kart->getWheelGraphicsPosition(3).getX(), 0.05f, + kart->getWheelGraphicsPosition(3).getZ()-0.1f ); + Vec3 rear_right(kart->getWheelGraphicsPosition(2).getX(), 0.05f, + kart->getWheelGraphicsPosition(2).getZ()-0.1f ); Vec3 rear_center(0, kart->getKartHeight()*0.35f, -kart->getKartLength()*0.35f); @@ -62,15 +53,12 @@ KartGFX::KartGFX(const AbstractKart *kart) -kart->getKartLength()*0.1f); // FIXME Used to match the emitter as seen in blender - const float delta = 0.6f; - Vec3 rear_nitro_right(kart->getNitroEmitterPosition(0).toIrrVector().X, - kart->getNitroEmitterPosition(0).toIrrVector().Y, - kart->getNitroEmitterPosition(0).toIrrVector().Z + delta); - - Vec3 rear_nitro_left(kart->getNitroEmitterPosition(1).toIrrVector().X, - kart->getNitroEmitterPosition(1).toIrrVector().Y, - kart->getNitroEmitterPosition(1).toIrrVector().Z + delta); - if (!kart->hasNitroEmitter()) + const Vec3 delta(0, 0, 0.6f); + Vec3 rear_nitro_right = kart->getKartModel()->getNitroEmittersPositon(0) + + delta; + Vec3 rear_nitro_left = kart->getKartModel()->getNitroEmittersPositon(1) + + delta; + if (!kart->getKartModel()->hasNitroEmitters()) rear_nitro_right = rear_nitro_left = rear_nitro_center; // Create all effects. Note that they must be created diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index 24d178760..19c122485 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index f34f57b45..20419c581 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,10 +34,43 @@ #include "utils/constants.hpp" #include "utils/log.hpp" +#include "IMeshManipulator.h" + #define SKELETON_DEBUG 0 float KartModel::UNDEFINED = -99.9f; +// ------------------------------------------------------------ +// SpeedWeightedObject implementation + +#define SPEED_WEIGHTED_OBJECT_PROPERTY_UNDEFINED -99.f + +SpeedWeightedObject::Properties::Properties() +{ + m_strength_factor = m_speed_factor = m_texture_speed.X = m_texture_speed.Y = SPEED_WEIGHTED_OBJECT_PROPERTY_UNDEFINED; +} + +void SpeedWeightedObject::Properties::loadFromXMLNode(const XMLNode* xml_node) +{ + xml_node->get("strength-factor", &m_strength_factor); + xml_node->get("speed-factor", &m_speed_factor); + xml_node->get("texture-speed-x", &m_texture_speed.X); + xml_node->get("texture-speed-y", &m_texture_speed.Y); +} + +void SpeedWeightedObject::Properties::checkAllSet() +{ +#define CHECK_NEG( a,strA) if(a<=SPEED_WEIGHTED_OBJECT_PROPERTY_UNDEFINED) { \ + Log::fatal("SpeedWeightedObject", "Missing default value for '%s'.", \ + strA); \ + } + CHECK_NEG(m_strength_factor, "speed-weighted strength-factor" ); + CHECK_NEG(m_speed_factor, "speed-weighted speed-factor" ); + CHECK_NEG(m_texture_speed.X, "speed-weighted texture speed X" ); + CHECK_NEG(m_texture_speed.Y, "speed-weighted texture speed Y" ); +#undef CHECK_NEG +} + /** Default constructor which initialises all variables with defaults. * Note that the KartModel is copied, so make sure to update makeCopy * if any more variables are added to this object. @@ -148,25 +181,21 @@ void KartModel::loadInfo(const XMLNode &node) m_has_nitro_emitter = true; } - if(const XMLNode *speedWeighted_node=node.getNode("speed-weighted")) + if(const XMLNode *speed_weighted_objects_node=node.getNode("speed-weighted-objects")) { - for(unsigned int i=0 ; i < speedWeighted_node->getNumNodes() ; i++) - { - loadSpeedWeightedInfo(speedWeighted_node->getNode(i)); - } + SpeedWeightedObject::Properties fallback_properties; + fallback_properties.loadFromXMLNode(speed_weighted_objects_node); + + for(unsigned int i=0 ; i < speed_weighted_objects_node->getNumNodes() ; i++) + { + loadSpeedWeightedInfo(speed_weighted_objects_node->getNode(i), fallback_properties); + } } if(const XMLNode *hat_node=node.getNode("hat")) { - if(hat_node->get("offset", &m_hat_offset)) - { - // Xmas mode handling :) - if(UserConfigParams::m_xmas_enabled) - setHatMeshName("christmas_hat.b3d"); - } + hat_node->get("offset", &m_hat_offset); } - else - m_hat_offset = core::vector3df(0,0,0); } // loadInfo // ---------------------------------------------------------------------------- @@ -249,7 +278,8 @@ KartModel* KartModel::makeCopy() km->m_kart_width = m_kart_width; km->m_kart_length = m_kart_length; km->m_kart_height = m_kart_height; - km->m_kart_highest_point = m_kart_highest_point; + km->m_kart_highest_point= m_kart_highest_point; + km->m_kart_lowest_point = m_kart_lowest_point; km->m_mesh = m_mesh; km->m_model_filename = m_model_filename; km->m_animation_speed = m_animation_speed; @@ -279,11 +309,9 @@ KartModel* KartModel::makeCopy() km->m_speed_weighted_objects.resize(m_speed_weighted_objects.size()); for(size_t i=0; im_speed_weighted_objects[i].m_model = m_speed_weighted_objects[i].m_model; // Master should not have any speed weighted nodes. assert(!m_speed_weighted_objects[i].m_node); - km->m_speed_weighted_objects[i].m_name = m_speed_weighted_objects[i].m_name; - km->m_speed_weighted_objects[i].m_position = m_speed_weighted_objects[i].m_position; + km->m_speed_weighted_objects[i] = m_speed_weighted_objects[i]; } for(unsigned int i=AF_BEGIN; i<=AF_END; i++) @@ -325,7 +353,7 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) std::string debug_name = m_model_filename+" (animated-kart-model)"; node->setName(debug_name.c_str()); #if SKELETON_DEBUG - irr_driber->addDebugMesh(m_animated_node); + irr_driver->addDebugMesh(m_animated_node); #endif #endif m_animated_node->setLoopMode(false); @@ -345,6 +373,15 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) if(!m_speed_weighted_objects[i].m_node) continue; m_speed_weighted_objects[i].m_node->setParent(lod_node); } + + // Enable rim lighting for the kart + irr_driver->applyObjectPassShader(lod_node, true); + std::vector &lodnodes = lod_node->getAllNodes(); + const u32 max = lodnodes.size(); + for (u32 i = 0; i < max; i++) + { + irr_driver->applyObjectPassShader(lodnodes[i], true); + } } else { @@ -368,7 +405,12 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) { if(!m_wheel_model[i]) continue; m_wheel_node[i] = irr_driver->addMesh(m_wheel_model[i], node); + Vec3 wheel_min, wheel_max; + MeshTools::minMax3D(m_wheel_model[i], &wheel_min, &wheel_max); + m_wheel_graphics_radius[i] = 0.5f*(wheel_max.getY() - wheel_min.getY()); + m_wheel_node[i]->grab(); + ((scene::IMeshSceneNode *) m_wheel_node[i])->setReadOnlyMaterials(true); #ifdef DEBUG std::string debug_name = m_wheel_filename[i]+" (wheel)"; m_wheel_node[i]->setName(debug_name.c_str()); @@ -420,9 +462,29 @@ bool KartModel::loadModels(const KartProperties &kart_properties) irr_driver->grabAllTextures(m_mesh); Vec3 kart_min, kart_max; - MeshTools::minMax3D(m_mesh->getMesh(m_animation_frame[AF_STRAIGHT]), &kart_min, &kart_max); + MeshTools::minMax3D(m_mesh->getMesh(m_animation_frame[AF_STRAIGHT]), + &kart_min, &kart_max); - m_kart_highest_point = kart_max.getY(); +#undef MOVE_KART_MESHES +#ifdef MOVE_KART_MESHES + // Kart models are not exactly centered. The following code would + // transform the mesh so that they are properly centered, but it + // would also mean all location relative to the original kart's + // center (wheel position, emitter, hat) would need to be modified. + scene::IMeshManipulator *mani = + irr_driver->getVideoDriver()->getMeshManipulator(); + Vec3 offset_from_center = -0.5f*(kart_max+kart_min); + offset_from_center.setY(-kart_min.getY()); + offset_from_center.setY(0); + + core::matrix4 translate(core::matrix4::EM4CONST_IDENTITY); + translate.setTranslation(offset_from_center.toIrrVector()); + mani->transform(m_mesh, translate); + MeshTools::minMax3D(m_mesh->getMesh(m_animation_frame[AF_STRAIGHT]), + &kart_min, &kart_max); +#endif + m_kart_highest_point = kart_max.getY(); + m_kart_lowest_point = kart_min.getY(); // Load the speed weighted object models. We need to do that now because it can affect the dimensions of the kart for(size_t i=0 ; i < m_speed_weighted_objects.size() ; i++) @@ -438,11 +500,11 @@ bool KartModel::loadModels(const KartProperties &kart_properties) // Update min/max Vec3 obj_min, obj_max; - MeshTools::minMax3D(obj.m_model, &obj_min, &obj_max); + MeshTools::minMax3D(obj.m_model, &obj_min, &obj_max); obj_min += obj.m_position; obj_max += obj.m_position; kart_min.min(obj_min); - kart_max.min(obj_max); + kart_max.max(obj_max); } Vec3 size = kart_max-kart_min; @@ -511,9 +573,11 @@ void KartModel::loadNitroEmitterInfo(const XMLNode &node, } // loadNitroEmitterInfo /** Loads a single speed weighted node. */ -void KartModel::loadSpeedWeightedInfo(const XMLNode* speed_weighted_node) +void KartModel::loadSpeedWeightedInfo(const XMLNode* speed_weighted_node, const SpeedWeightedObject::Properties& fallback_properties) { SpeedWeightedObject obj; + obj.m_properties = fallback_properties; + obj.m_properties.loadFromXMLNode(speed_weighted_node); speed_weighted_node->get("position", &obj.m_position); speed_weighted_node->get("model", &obj.m_name); @@ -592,7 +656,7 @@ void KartModel::reset() { // Reset the wheels const float suspension[4]={0,0,0,0}; - update(0, 0.0f, suspension, 0.0f); + update(0.0f, 0.0f, 0.0f, suspension, 0.0f); // Stop any animations currently being played. setAnimation(KartModel::AF_DEFAULT); @@ -700,35 +764,16 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node) // ---------------------------------------------------------------------------- /** Rotates and turns the wheels appropriately, and adjust for suspension + updates the speed-weighted objects' animations. + * \param dt time since last frame * \param rotation_dt How far the wheels have rotated since last time. * \param steer The actual steer settings. * \param suspension Suspension height for all four wheels. * \param speed The speed of the kart in meters/sec, used for the speed-weighted objects' animations */ -void KartModel::update(float rotation_dt, float steer, const float suspension[4], float speed) +void KartModel::update(float dt, float rotation_dt, float steer, + const float height_above_terrain[4], float speed) { - float clamped_suspension[4]; - // Clamp suspension to minimum and maximum suspension length, so that - // the graphical wheel models don't look too wrong. - for(unsigned int i=0; i<4; i++) - { - const float suspension_length = (m_max_suspension[i]-m_min_suspension[i])/2; - - // limit amplitude between set limits, first dividing it by a - // somewhat arbitrary constant to reduce visible wheel movement - clamped_suspension[i] = suspension[i]/m_dampen_suspension_amplitude[i]; - float ratio = clamped_suspension[i] / suspension_length; - const int sign = ratio < 0 ? -1 : 1; - // expanded form of 1 - (1 - x)^2, i.e. making suspension display - // quadratic and not linear - ratio = sign * fabsf(ratio*(2-ratio)); -// clamped_suspension[i] = ratio*suspension_length; - clamped_suspension[i] = std::min(std::max(ratio*suspension_length, - m_min_suspension[i]), - m_max_suspension[i]); - } // for i<4 - - core::vector3df wheel_steer(0, steer*30.0f, 0); + core::vector3df wheel_steer(0, steer*30.0f, 0); for(unsigned int i=0; i<4; i++) { @@ -744,7 +789,8 @@ void KartModel::update(float rotation_dt, float steer, const float suspension[4] } #endif core::vector3df pos = m_wheel_graphics_position[i].toIrrVector(); - pos.Y += clamped_suspension[i]; + pos.Y = m_kart_lowest_point - height_above_terrain[i] + + m_wheel_graphics_radius[i]; m_wheel_node[i]->setPosition(pos); // Now calculate the new rotation: (old + change) mod 360 @@ -765,12 +811,52 @@ void KartModel::update(float rotation_dt, float steer, const float suspension[4] for(size_t i=0 ; i < m_speed_weighted_objects.size() ; i++) { SpeedWeightedObject& obj = m_speed_weighted_objects[i]; - float strength = speed * m_kart->getKartProperties()->getSpeedWeightedStrengthFactor(); - btClamp(strength, 0.0f, 1.0f); - obj.m_node->setAnimationStrength(strength); - float anim_speed = speed * m_kart->getKartProperties()->getSpeedWeightedSpeedFactor(); - obj.m_node->setAnimationSpeed(anim_speed); +#define GET_VALUE(obj, value_name) \ + obj.m_properties.value_name > SPEED_WEIGHTED_OBJECT_PROPERTY_UNDEFINED ? obj.m_properties.value_name : \ + m_kart->getKartProperties()->getSpeedWeightedObjectProperties().value_name + + // Animation strength + float strength = 1.0f; + const float strength_factor = GET_VALUE(obj, m_strength_factor); + if(strength_factor >= 0.0f) + { + strength = speed * strength_factor; + btClamp(strength, 0.0f, 1.0f); + } + obj.m_node->setAnimationStrength(strength); + + // Animation speed + const float speed_factor = GET_VALUE(obj, m_speed_factor); + if(speed_factor >= 0.0f) + { + float anim_speed = speed * speed_factor; + obj.m_node->setAnimationSpeed(anim_speed); + } + + // Texture animation + core::vector2df tex_speed; + tex_speed.X = GET_VALUE(obj, m_texture_speed.X); + tex_speed.Y = GET_VALUE(obj, m_texture_speed.Y); + if(tex_speed != core::vector2df(0.0f, 0.0f)) + { + obj.m_texture_cur_offset += speed * tex_speed * dt; + if(obj.m_texture_cur_offset.X > 1.0f) obj.m_texture_cur_offset.X = fmod(obj.m_texture_cur_offset.X, 1.0f); + if(obj.m_texture_cur_offset.Y > 1.0f) obj.m_texture_cur_offset.Y = fmod(obj.m_texture_cur_offset.Y, 1.0f); + + for(unsigned int i=0; igetMaterialCount(); i++) + { + video::SMaterial &irrMaterial=obj.m_node->getMaterial(i); + for(unsigned int j=0; jsetTextureTranslate(obj.m_texture_cur_offset.X, obj.m_texture_cur_offset.Y); + } // for j0) + m_hat_node = NULL; + if(m_hat_name.size()>0) + { + scene::IBoneSceneNode *bone = m_animated_node->getJointNode("Head"); + if(!bone) + bone = m_animated_node->getJointNode("head"); + if(bone) { - scene::IBoneSceneNode *bone = m_animated_node->getJointNode("Head"); - if(!bone) - bone = m_animated_node->getJointNode("head"); - if(bone) - { - - // Till we have all models fixed, accept Head and head as bone naartme - scene::IMesh *hat_mesh = - irr_driver->getAnimatedMesh( - file_manager->getModelFile(m_hat_name)); - m_hat_node = irr_driver->addMesh(hat_mesh); - bone->addChild(m_hat_node); - m_animated_node->setCurrentFrame((float)m_animation_frame[AF_STRAIGHT]); - m_animated_node->OnAnimate(0); - bone->updateAbsolutePosition(); - - // With the hat node attached to the head bone, we have to - // reverse the transformation of the bone, so that the hat - // is still properly placed. Esp. the hat offset needs - // to be rotated. - const core::matrix4 mat = bone->getAbsoluteTransformation(); - core::matrix4 inv; - mat.getInverse(inv); - core::vector3df rotated_offset; - inv.rotateVect(rotated_offset, m_hat_offset); - m_hat_node->setPosition(rotated_offset); - m_hat_node->setScale(inv.getScale()); - m_hat_node->setRotation(inv.getRotationDegrees()); - } // if bone - } // if(m_hat_name) + // Till we have all models fixed, accept Head and head as bone naartme + scene::IMesh *hat_mesh = + irr_driver->getAnimatedMesh( + file_manager->getModelFile(m_hat_name)); + m_hat_node = irr_driver->addMesh(hat_mesh); + bone->addChild(m_hat_node); + m_animated_node->setCurrentFrame((float)m_animation_frame[AF_STRAIGHT]); + m_animated_node->OnAnimate(0); + bone->updateAbsolutePosition(); + // With the hat node attached to the head bone, we have to + // reverse the transformation of the bone, so that the hat + // is still properly placed. Esp. the hat offset needs + // to be rotated. + const core::matrix4 mat = bone->getAbsoluteTransformation(); + core::matrix4 inv; + mat.getInverse(inv); + core::vector3df rotated_offset; + inv.rotateVect(rotated_offset, m_hat_offset); + m_hat_node->setPosition(rotated_offset); + m_hat_node->setScale(inv.getScale()); + m_hat_node->setRotation(inv.getRotationDegrees()); + } // if bone + } // if(m_hat_name) } diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index f873553b8..bc01dafc2 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -37,8 +37,28 @@ class AbstractKart; class KartProperties; class XMLNode; +/** A speed-weighted object is an object whose characteristics are influenced by the kart's speed */ struct SpeedWeightedObject { + /** Parameters for a speed-weighted object */ + struct Properties + { + Properties(); + + /** Strength factor: how much the kart speed affects the animation's distance from a static pose (-1 to disable) */ + float m_strength_factor; + + /** Speed factor: how much the kart speed affects the animation's speed (-1 to disable) */ + float m_speed_factor; + + /** Texture speed, in UV coordinates */ + core::vector2df m_texture_speed; + + void loadFromXMLNode(const XMLNode* xml_node); + + void checkAllSet(); + }; + SpeedWeightedObject() : m_model(NULL), m_node(NULL), m_position(), m_name() {} /** Model */ scene::IAnimatedMesh * m_model; @@ -51,6 +71,13 @@ struct SpeedWeightedObject /** Filename of the "speed weighted" object */ std::string m_name; + + /** Current uv translation in the texture matrix for speed-weighted texture animations */ + core::vector2df m_texture_cur_offset; + + /** Specific properties for this given speed-weighted object, + * otherwise just a copy of the values from the kart's properties */ + Properties m_properties; }; typedef std::vector SpeedWeightedObjectList; @@ -163,10 +190,21 @@ private: * to AF_DEFAULT the default steering animation is shown. */ AnimationFrameType m_current_animation; - float m_kart_width; /**< Width of kart. */ - float m_kart_length; /**< Length of kart. */ - float m_kart_height; /**< Height of kart. */ - float m_kart_highest_point; /**< Coordinate on up axis */ + /** Width of kart. */ + float m_kart_width; + + /** Length of kart. */ + float m_kart_length; + + /** Height of kart. */ + float m_kart_height; + + /** Largest coordinate on up axis. */ + float m_kart_highest_point; + + /** Smallest coordinate on up axis. */ + float m_kart_lowest_point; + /** True if this is the master copy, managed by KartProperties. This * is mainly used for debugging, e.g. the master copies might not have * anything attached to it etc. */ @@ -178,7 +216,8 @@ private: void loadNitroEmitterInfo(const XMLNode &node, const std::string &emitter_name, int index); - void loadSpeedWeightedInfo(const XMLNode* speed_weighted_node); + void loadSpeedWeightedInfo(const XMLNode* speed_weighted_node, + const SpeedWeightedObject::Properties& fallback_properties); void OnAnimationEnd(scene::IAnimatedMeshSceneNode *node); @@ -192,8 +231,8 @@ public: void reset(); void loadInfo(const XMLNode &node); bool loadModels(const KartProperties &kart_properties); - void update(float rotation_dt, float steer, - const float suspension[4], float speed); + void update(float dt, float rotation_dt, float steer, + const float height_abve_terrain[4], float speed); void setDefaultPhysicsPosition(const Vec3 ¢er_shift, float wheel_radius); void finishedRace(); @@ -239,9 +278,10 @@ public: {assert(i>=0 && i<4); return m_wheel_graphics_radius[i]; } // ------------------------------------------------------------------------ /** Returns the position of nitro emitter relative to the kart. + * \param i Index of the emitter: 0 = right, 1 = left */ - const Vec3* getNitroEmittersPositon() const - {return m_nitro_emitter_position;} + const Vec3& getNitroEmittersPositon(unsigned int i) const + { assert(i>=0 && i<2); return m_nitro_emitter_position[i]; } // ------------------------------------------------------------------------ /** Returns true if kart has nitro emitters */ const bool hasNitroEmitters() const @@ -264,9 +304,12 @@ public: // ------------------------------------------------------------------------ /** Returns the height of the kart. */ float getHeight () const {return m_kart_height; } - // ------------------------------------------------------------------------ - /** Coordoinate on up axis */ - float getHighestPoint () const { return m_kart_highest_point; } + // ------------------------------------------------------------------------ + /** Highest coordinate on up axis */ + float getHighestPoint () const { return m_kart_highest_point; } + // ------------------------------------------------------------------------ + /** Lowest coordinate on up axis */ + float getLowestPoint () const { return m_kart_lowest_point; } // ------------------------------------------------------------------------ /** Enables- or disables the end animation. */ void setAnimation(AnimationFrameType type); @@ -276,15 +319,15 @@ public: // ------------------------------------------------------------------------ /** Name of the hat mesh to use. */ void setHatMeshName(const std::string &name) {m_hat_name = name; } - // ------------------------------------------------------------------------ - void attachHat(); + // ------------------------------------------------------------------------ + void attachHat(); // ------------------------------------------------------------------------ /** Returns the array of wheel nodes. */ scene::ISceneNode** getWheelNodes() { return m_wheel_node; } - // ------------------------------------------------------------------------ - scene::IAnimatedMeshSceneNode* getAnimatedNode(){ return m_animated_node; } - // ------------------------------------------------------------------------ - core::vector3df getHatOffset() { return m_hat_offset; } + // ------------------------------------------------------------------------ + scene::IAnimatedMeshSceneNode* getAnimatedNode(){ return m_animated_node; } + // ------------------------------------------------------------------------ + core::vector3df getHatOffset() { return m_hat_offset; } }; // KartModel #endif diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 0961d0e94..3ac459622 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -68,8 +68,7 @@ KartProperties::KartProperties(const std::string &filename) m_nitro_max_speed_increase = m_nitro_duration = m_nitro_fade_out_time = m_suspension_stiffness = m_wheel_damping_relaxation = m_wheel_base = m_wheel_damping_compression = m_friction_slip = m_roll_influence = - m_wheel_radius = m_speed_weighted_strength_factor = m_speed_weighted_speed_factor = - m_chassis_linear_damping = m_max_suspension_force = + m_wheel_radius = m_chassis_linear_damping = m_max_suspension_force = m_chassis_angular_damping = m_suspension_rest = m_max_speed_reverse_ratio = m_rescue_vert_offset = m_upright_tolerance = m_collision_terrain_impulse = @@ -218,6 +217,9 @@ void KartProperties::load(const std::string &filename, const std::string &node) file_manager->pushModelSearchPath (m_root); file_manager->pushTextureSearchPath(m_root); + irr_driver->setTextureErrorMessage("Error while loading kart '%s':", + m_name); + // addShared makes sure that these textures/material infos stay in memory material_manager->addSharedMaterial(materials_file); @@ -270,6 +272,8 @@ void KartProperties::load(const std::string &filename, const std::string &node) } m_shadow_texture = irr_driver->getTexture(m_shadow_file); + + irr_driver->unsetTextureErrorMessage(); file_manager->popTextureSearchPath(); file_manager->popModelSearchPath(); @@ -427,10 +431,9 @@ void KartProperties::getAllData(const XMLNode * root) wheels_node->get("radius", &m_wheel_radius ); } - if(const XMLNode *speed_weighted_node = root->getNode("speed-weighted")) + if(const XMLNode *speed_weighted_objects_node = root->getNode("speed-weighted-objects")) { - speed_weighted_node->get("strength-factor", &m_speed_weighted_strength_factor); - speed_weighted_node->get("speed-factor", &m_speed_weighted_speed_factor); + m_speed_weighted_object_properties.loadFromXMLNode(speed_weighted_objects_node); } if(const XMLNode *friction_node = root->getNode("friction")) @@ -633,8 +636,6 @@ void KartProperties::checkAllSet(const std::string &filename) CHECK_NEG(m_time_reset_steer, "turn time-reset-steer" ); CHECK_NEG(m_wheel_damping_relaxation, "wheels damping-relaxation" ); CHECK_NEG(m_wheel_damping_compression, "wheels damping-compression" ); - CHECK_NEG(m_speed_weighted_strength_factor, "speed-weighted strength-factor" ); - CHECK_NEG(m_speed_weighted_speed_factor, "speed-weighted speed-factor" ); CHECK_NEG(m_wheel_radius, "wheels radius" ); CHECK_NEG(m_friction_slip, "friction slip" ); CHECK_NEG(m_roll_influence, "stability roll-influence" ); @@ -716,6 +717,8 @@ void KartProperties::checkAllSet(const std::string &filename) CHECK_NEG(m_plunger_in_face_duration[i],"plunger in-face-time"); } + m_speed_weighted_object_properties.checkAllSet(); + m_skidding_properties->checkAllSet(filename); for(unsigned int i=0; icheckAllSet(filename); diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index 75cd08316..bbadd2230 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -264,9 +264,8 @@ private: float m_roll_influence; float m_wheel_radius; - // Parameters for speed-weighted objects - float m_speed_weighted_strength_factor; - float m_speed_weighted_speed_factor; + /** Parameters for the speed-weighted objects */ + SpeedWeightedObject::Properties m_speed_weighted_object_properties; /** An impulse pushing the kart down which is proportional to speed. So * the actual impulse is speed * m_downward_impulse_factor. Set it to @@ -420,6 +419,14 @@ public: * should not be modified, not attachModel be called on it. */ const KartModel& getMasterKartModel() const {return *m_kart_model; } + // ------------------------------------------------------------------------ + /** Sets the name of a mesh to be used for this kart. + * \param hat_name Name of the mesh. + */ + void setHatMeshName(const std::string &hat_name) + { + m_kart_model->setHatMeshName(hat_name); + } // setHatMeshName // ------------------------------------------------------------------------ /** Returns the name of this kart. \note Pass it through fridibi as needed, this is the LTR name @@ -533,12 +540,8 @@ public: float getWheelRadius () const {return m_wheel_radius; } // ------------------------------------------------------------------------ - /** Returns animation strength factor for speed-weighted objects */ - float getSpeedWeightedStrengthFactor() const {return m_speed_weighted_strength_factor;} - - // ------------------------------------------------------------------------ - /** Returns animation speed factor for speed-weighted objects */ - float getSpeedWeightedSpeedFactor() const {return m_speed_weighted_speed_factor;} + /** Returns parameters for the speed-weighted objects */ + const SpeedWeightedObject::Properties& getSpeedWeightedObjectProperties() const {return m_speed_weighted_object_properties;} // ------------------------------------------------------------------------ /** Returns the wheel base (distance front to rear axis). */ diff --git a/src/karts/kart_properties_manager.cpp b/src/karts/kart_properties_manager.cpp index 0c58555b9..a1d0cbd3a 100644 --- a/src/karts/kart_properties_manager.cpp +++ b/src/karts/kart_properties_manager.cpp @@ -1,6 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2006 Ingo Ruhnke +// +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -191,7 +192,7 @@ bool KartPropertiesManager::loadKart(const std::string &dir) { std::string config_filename=dir+"/kart.xml"; if(!file_manager->fileExists(config_filename)) - return false; + return false; KartProperties* kart_properties; try @@ -232,6 +233,18 @@ bool KartPropertiesManager::loadKart(const std::string &dir) return true; } // loadKartData +//----------------------------------------------------------------------------- +/** Sets the name of a mesh to use as a hat for all karts. + * \param hat_name Name of the hat mash. + */ +void KartPropertiesManager::setHatMeshName(const std::string &hat_name) +{ + for (int i=0; i +// +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -87,6 +88,7 @@ public: void getRandomKartList(int count, RemoteKartInfoList& existing_karts, std::vector *ai_list); + void setHatMeshName(const std::string &hat_name); // ------------------------------------------------------------------------ /** Returns a list of all groups. */ const std::vector& getAllGroups() const {return m_all_groups;} diff --git a/src/karts/kart_with_stats.cpp b/src/karts/kart_with_stats.cpp index d1620119b..455b9df5f 100644 --- a/src/karts/kart_with_stats.cpp +++ b/src/karts/kart_with_stats.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/kart_with_stats.hpp b/src/karts/kart_with_stats.hpp index 5087350a5..8c5544a6e 100644 --- a/src/karts/kart_with_stats.hpp +++ b/src/karts/kart_with_stats.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index cacde5a88..9dff0781a 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/max_speed.hpp b/src/karts/max_speed.hpp index c57cb0f64..63ddbefca 100644 --- a/src/karts/max_speed.hpp +++ b/src/karts/max_speed.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index 5a5c6c225..afeb56e4c 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -17,6 +17,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include #include "karts/moveable.hpp" #include "graphics/irr_driver.hpp" diff --git a/src/karts/moveable.hpp b/src/karts/moveable.hpp index 4461620ee..42d82217a 100644 --- a/src/karts/moveable.hpp +++ b/src/karts/moveable.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -31,13 +31,14 @@ using namespace irr; #include "physics/user_pointer.hpp" #include "utils/no_copy.hpp" #include "utils/vec3.hpp" +#include "network/types.hpp" class Material; /** * \ingroup karts */ -class Moveable: public NoCopy +class Moveable: public NoCopy, public CallbackObject { private: btVector3 m_velocityLC; /**getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); + m_kart->getControls().m_skid = KartControl::SC_NONE; } // reset // ---------------------------------------------------------------------------- @@ -121,7 +122,6 @@ void Skidding::updateSteering(float steer, float dt) else if (m_visual_rotation < -0.05f) m_visual_rotation += 0.05f; else { - m_visual_rotation = 0; reset(); } break; @@ -212,7 +212,8 @@ void Skidding::update(float dt, bool is_on_ground, } // No skidding backwards or while stopped - if(m_kart->getSpeed() < 0.001f) + if(m_kart->getSpeed() < 0.001f && + m_skid_state != SKID_NONE && m_skid_state != SKID_BREAK) { m_skid_state = SKID_BREAK; m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); diff --git a/src/karts/skidding.hpp b/src/karts/skidding.hpp index 734b53e70..83afb927e 100644 --- a/src/karts/skidding.hpp +++ b/src/karts/skidding.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/skidding_properties.cpp b/src/karts/skidding_properties.cpp index 425dc834c..1bf1a246e 100644 --- a/src/karts/skidding_properties.cpp +++ b/src/karts/skidding_properties.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/karts/skidding_properties.hpp b/src/karts/skidding_properties.hpp index e5e2c30af..ef1bc7042 100644 --- a/src/karts/skidding_properties.hpp +++ b/src/karts/skidding_properties.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/main.cpp b/src/main.cpp index 6ba9231bb..9536da65b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2006 Steve Baker -// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2011-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -120,7 +120,9 @@ # ifdef __CYGWIN__ # include # endif +# define WIN32_LEAN_AND_MEAN # define _WINSOCKAPI_ +# define WIN32_LEAN_AND_MEAN # include # ifdef _MSC_VER # include @@ -128,7 +130,6 @@ #else # include #endif -#include #include #include #include @@ -139,6 +140,7 @@ #include #include "main_loop.hpp" +#include "achievements/achievements_manager.hpp" #include "addons/addons_manager.hpp" #include "addons/inetwork_http.hpp" #include "addons/news_manager.hpp" @@ -155,6 +157,7 @@ #include "graphics/referee.hpp" #include "guiengine/engine.hpp" #include "guiengine/event_handler.hpp" +#include "guiengine/dialog_queue.hpp" #include "input/input_manager.hpp" #include "input/device_manager.hpp" #include "input/wiimote_manager.hpp" @@ -168,6 +171,20 @@ #include "modes/demo_world.hpp" #include "modes/profile_world.hpp" #include "network/network_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/server_network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/server_lobby_room_protocol.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/server_network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/server_lobby_room_protocol.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "online/profile_manager.hpp" +#include "online/servers_manager.hpp" #include "race/grand_prix_manager.hpp" #include "race/highscore_manager.hpp" #include "race/history.hpp" @@ -180,6 +197,7 @@ #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/constants.hpp" +#include "utils/crash_reporting.hpp" #include "utils/leak_check.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" @@ -339,11 +357,38 @@ void gamepadVisualisation() driver->endScene(); } -} +} // gamepadVisualisation + // ============================================================================ +/** Sets the Christmas flag (m_xmas_enabled), depending on currently set + * Christ mode (m_xmas_mode) + */ +void handleXmasMode() +{ + bool xmas = false; + switch(UserConfigParams::m_xmas_mode) + { + case 0: + { + int day, month; + StkTime::getDate(&day, &month); + // Christmat hats are shown between 17. of December + // and 5th of January + xmas = (month == 12 && day>=17) || (month == 1 && day <=5); + break; + } + case 1: xmas = true; break; + default: xmas = false; break; + } // switch m_xmas_mode + if(xmas) + kart_properties_manager->setHatMeshName("christmas_hat.b3d"); +} // handleXmasMode -void cmdLineHelp (char* invocation) +// ---------------------------------------------------------------------------- +/** Prints help for command line options to stdout. + */ +void cmdLineHelp(char* invocation) { Log::info("main", "Usage: %s [OPTIONS]\n\n" @@ -390,6 +435,7 @@ void cmdLineHelp (char* invocation) " --profile-time=n Enable automatic driven profile mode for n " "seconds.\n" " --no-graphics Do not display the actual race.\n" + " --with-profile Enables the profile mode.\n" " --demo-mode t Enables demo mode after t seconds idle time in " "main menu.\n" " --demo-tracks t1,t2 List of tracks to be used in demo mode. No\n" @@ -401,13 +447,11 @@ void cmdLineHelp (char* invocation) // " --history=n Replay history file 'history.dat' using:\n" // " n=1: recorded positions\n" // " n=2: recorded key strokes\n" - //" --server[=port] This is the server (running on the specified " - // "port).\n" - //" --client=ip This is a client, connect to the specified ip" - // " address.\n" - //" --port=n Port number to use.\n" - //" --numclients=n Number of clients to wait for (server " - // "only).\n" + " --server Start a server (not a playing client).\n" + " --login=s Automatically sign in (set the login).\n" + " --password=s Automatically sign in (set the password).\n" + " --port=n Port number to use.\n" + " --max-players=n Maximum number of clients (server only).\n" " --no-console Does not write messages in the console but to\n" " stdout.log.\n" " --console Write messages in the console and files\n" @@ -466,14 +510,7 @@ int handleCmdLinePreliminary(int argc, char **argv) } else if ( sscanf(argv[i], "--xmas=%d", &n) ) { - if (n) - { - UserConfigParams::m_xmas_enabled = true; - } - else - { - UserConfigParams::m_xmas_enabled = false; - } + UserConfigParams::m_xmas_mode = n; } else if( !strcmp(argv[i], "--no-console")) { @@ -592,7 +629,7 @@ int handleCmdLinePreliminary(int argc, char **argv) } // --verbose or -v } return 0; -} +} // handleCmdLinePreliminary // ============================================================================ /** Handles command line options. @@ -604,6 +641,9 @@ int handleCmdLine(int argc, char **argv) int n; char s[1024]; + bool try_login = false; + irr::core::stringw login, password; + for(int i=1; isetMode(NetworkManager::NW_SERVER); UserConfigParams::m_server_port = n; } else if( !strcmp(argv[i], "--server") ) { - network_manager->setMode(NetworkManager::NW_SERVER); + NetworkManager::getInstance(); + Log::info("main", "Creating a server network manager."); } - else if( sscanf(argv[i], "--port=%d", &n) ) + else if( sscanf(argv[i], "--max-players=%d", &n) ) { - UserConfigParams::m_server_port=n; + UserConfigParams::m_server_max_players=n; } - else if( sscanf(argv[i], "--client=%1023s", s) ) + else if( sscanf(argv[i], "--login=%1023s", s) ) { - network_manager->setMode(NetworkManager::NW_CLIENT); - UserConfigParams::m_server_address=s; + login = s; + try_login = true; + } + else if( sscanf(argv[i], "--password=%1023s", s) ) + { + password = s; } else if ( sscanf(argv[i], "--gfx=%d", &n) ) { @@ -982,6 +1031,9 @@ int handleCmdLine(int argc, char **argv) race_manager->setNumLaps(999999); // profile end depends on time } else if( !strcmp(argv[i], "--no-graphics") ) + { + } + else if( !strcmp(argv[i], "--with-profile") ) { // Set default profile mode of 1 lap if we haven't already set one if (!ProfileWorld::isProfileMode()) { @@ -1097,6 +1149,20 @@ int handleCmdLine(int argc, char **argv) UserConfigParams::m_music = false;// and music when profiling } + if (try_login) + { + irr::core::stringw s; + Online::CurrentUser::SignInRequest* request = + Online::CurrentUser::get()->requestSignIn(login, password, false, false); + + Online::HTTPManager::get()->synchronousRequest(request); + + if (request->isSuccess()) + { + Log::info("Main", "Logged in from command line."); + } + } + return 1; } // handleCmdLine @@ -1108,8 +1174,7 @@ void initUserConfig(char *argv[]) irr_driver = new IrrDriver(); file_manager = new FileManager(argv); user_config = new UserConfig(); // needs file_manager - const bool config_ok = user_config->loadConfig(); - + const bool config_ok = user_config->loadConfig(); if (UserConfigParams::m_language.toString() != "system") { #ifdef WIN32 @@ -1124,7 +1189,7 @@ void initUserConfig(char *argv[]) translations = new Translations(); // needs file_manager stk_config = new STKConfig(); // in case of --stk-config // command line parameters - + user_config->postLoadInit(); if (!config_ok || UserConfigParams::m_all_players.size() == 0) { user_config->addDefaultPlayer(); @@ -1165,6 +1230,8 @@ void initRest() // to network_http (since the thread might use network_http, otherwise // a race condition can be introduced resulting in a crash). INetworkHttp::get()->startNetworkThread(); + Online::HTTPManager::get()->startNetworkThread(); + AchievementsManager::get()->init(); music_manager = new MusicManager(); sfx_manager = new SFXManager(); // The order here can be important, e.g. KartPropertiesManager needs @@ -1178,7 +1245,6 @@ void initRest() powerup_manager = new PowerupManager (); attachment_manager = new AttachmentManager (); highscore_manager = new HighscoreManager (); - network_manager = new NetworkManager (); KartPropertiesManager::addKartSearchDir( file_manager->getAddonsFile("karts/")); track_manager->addTrackSearchDir( @@ -1194,8 +1260,10 @@ void initRest() // Consistency check for challenges, and enable all challenges // that have all prerequisites fulfilled grand_prix_manager->checkConsistency(); - GUIEngine::addLoadingIcon( irr_driver->getTexture( - file_manager->getTextureFile("cup_gold.png")) ); + std::string file = file_manager->getTextureFile("cup_gold.png"); + if(file.size()==0) + Log::fatal("main", "Can not find cup_gold.png, aborting."); + GUIEngine::addLoadingIcon( irr_driver->getTexture(file) ); race_manager = new RaceManager (); // default settings for Quickstart @@ -1213,19 +1281,33 @@ void initRest() */ static void cleanSuperTuxKart() { + + delete main_loop; + irr_driver->updateConfigIfRelevant(); if(INetworkHttp::get()) INetworkHttp::get()->stopNetworkThread(); + if(Online::HTTPManager::isRunning()) + Online::HTTPManager::get()->stopNetworkThread(); + //delete in reverse order of what they were created in. //see InitTuxkart() + Online::HTTPManager::deallocate(); + Online::ServersManager::deallocate(); + Online::ProfileManager::deallocate(); + AchievementsManager::deallocate(); + Online::CurrentUser::deallocate(); + GUIEngine::DialogQueue::deallocate(); + Referee::cleanup(); if(ReplayPlay::get()) ReplayPlay::destroy(); if(race_manager) delete race_manager; INetworkHttp::destroy(); if(news_manager) delete news_manager; if(addons_manager) delete addons_manager; - if(network_manager) delete network_manager; + NetworkManager::kill(); + if(grand_prix_manager) delete grand_prix_manager; if(highscore_manager) delete highscore_manager; if(attachment_manager) delete attachment_manager; @@ -1241,19 +1323,6 @@ static void cleanSuperTuxKart() if(music_manager) delete music_manager; delete ParticleKindManager::get(); if(stk_config) delete stk_config; - -#ifndef WIN32 - if (user_config) //close logfiles - { - Log::closeOutputFiles(); -#endif - fclose(stderr); - fclose(stdout); -#ifndef WIN32 - } -#endif - - if(user_config) delete user_config; if(unlock_manager) delete unlock_manager; if(translations) delete translations; @@ -1278,15 +1347,6 @@ bool ShowDumpResults(const wchar_t* dump_path, } #endif -static bool checkXmasTime() -{ - time_t rawtime; - struct tm* timeinfo; - time(&rawtime); - timeinfo = localtime(&rawtime); - return (timeinfo->tm_mon == 12-1); // Xmas mode happens in December -} - #if defined(DEBUG) && defined(WIN32) && !defined(__CYGWIN__) #pragma comment(linker, "/SUBSYSTEM:console") #endif @@ -1297,6 +1357,8 @@ int main(int argc, char *argv[] ) google_breakpad::ExceptionHandler eh(L"C:\\Temp", NULL, ShowDumpResults, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL); #endif + CrashReporting::installHandlers(); + srand(( unsigned ) time( 0 )); try { @@ -1306,8 +1368,6 @@ int main(int argc, char *argv[] ) initUserConfig(argv); // argv passed so config file can be // found more reliably - UserConfigParams::m_xmas_enabled = checkXmasTime(); - handleCmdLinePreliminary(argc, argv); initRest(); @@ -1330,10 +1390,12 @@ int main(int argc, char *argv[] ) GUIEngine::addLoadingIcon( irr_driver->getTexture( file_manager->getGUIDir() + "options_video.png") ); kart_properties_manager -> loadAllKarts (); + handleXmasMode(); unlock_manager = new UnlockManager(); - //m_tutorial_manager = new TutorialManager(); - GUIEngine::addLoadingIcon( irr_driver->getTexture( - file_manager->getTextureFile("gui_lock.png")) ); + std::string file = file_manager->getTextureFile("gui_lock.png"); + if(file.size()==0) + Log::fatal("main", "Can not find gui_lock.png, aborting."); + GUIEngine::addLoadingIcon( irr_driver->getTexture(file)); projectile_manager -> loadData (); // Both item_manager and powerup_manager load models and therefore @@ -1368,6 +1430,18 @@ int main(int argc, char *argv[] ) //handleCmdLine() needs InitTuxkart() so it can't be called first if(!handleCmdLine(argc, argv)) exit(0); + // load the network manager + // If the server has been created (--server option), this will do nothing (just a warning): + NetworkManager::getInstance(); + if (NetworkManager::getInstance()->isServer()) + ServerNetworkManager::getInstance()->setMaxPlayers( + UserConfigParams::m_server_max_players); + NetworkManager::getInstance()->run(); + if (NetworkManager::getInstance()->isServer()) + { + ProtocolManager::getInstance()->requestStart(new ServerLobbyRoomProtocol()); + } + addons_manager->checkInstalledAddons(); // Load addons.xml to get info about addons even when not @@ -1380,7 +1454,18 @@ int main(int argc, char *argv[] ) } } - if(!UserConfigParams::m_no_start_screen) + // no graphics, and no profile mode + if (ProfileWorld::isNoGraphics() && !ProfileWorld::isProfileMode()) + { + // hack to have a running game slot : + PtrVector& players = UserConfigParams::m_all_players; + if (UserConfigParams::m_default_player.toString().size() > 0) + for (int n=0; nsetCurrentSlot(players[n].getUniqueID()); + + } + else if(!UserConfigParams::m_no_start_screen) { StateManager::get()->pushScreen(StoryModeLobbyScreen::getInstance()); #ifdef ENABLE_WIIUSE @@ -1442,7 +1527,7 @@ int main(int argc, char *argv[] ) // Create player and associate player with keyboard StateManager::get()->createActivePlayer( - UserConfigParams::m_all_players.get(0), device ); + UserConfigParams::m_all_players.get(0), device, NULL); if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL) { @@ -1465,7 +1550,6 @@ int main(int argc, char *argv[] ) StateManager::get()->enterGameState(); } - // If an important news message exists it is shown in a popup dialog. const core::stringw important_message = news_manager->getImportantMessage(); @@ -1483,7 +1567,7 @@ int main(int argc, char *argv[] ) { // This will setup the race manager etc. history->Load(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); main_loop->run(); // well, actually run() will never return, since @@ -1492,20 +1576,6 @@ int main(int argc, char *argv[] ) exit(-3); } - // Initialise connection in case that a command line option was set - // configuring a client or server. Otherwise this function does nothing - // here (and will be called again from the network gui). - if(!network_manager->initialiseConnections()) - { - Log::error("main", "Problems initialising network connections,\n" - "Running in non-network mode."); - } - // On the server start with the network information page for now - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - // TODO - network menu - //menu_manager->pushMenu(MENUID_NETWORK_GUI); - } // Not replaying // ============= if(!ProfileWorld::isProfileMode()) @@ -1515,7 +1585,7 @@ int main(int argc, char *argv[] ) // Quickstart (-N) // =============== // all defaults are set in InitTuxkart() - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } } @@ -1524,8 +1594,7 @@ int main(int argc, char *argv[] ) // Profiling // ========= race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE); - race_manager->setDifficulty(RaceManager::DIFFICULTY_HARD); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } main_loop->run(); @@ -1555,6 +1624,7 @@ int main(int argc, char *argv[] ) // so we don't crash later when StateManager tries to access input devices. StateManager::get()->resetActivePlayers(); if(input_manager) delete input_manager; // if early crash avoid delete NULL + NetworkManager::getInstance()->abort(); cleanSuperTuxKart(); @@ -1562,6 +1632,19 @@ int main(int argc, char *argv[] ) MemoryLeaks::checkForLeaks(); #endif +#ifndef WIN32 + if (user_config) //close logfiles + { + Log::closeOutputFiles(); +#endif + fclose(stderr); + fclose(stdout); +#ifndef WIN32 + } +#endif + + + return 0 ; } // main diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 8524dbed0..e75624411 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Ingo Ruhnke +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -29,7 +30,9 @@ #include "input/wiimote_manager.hpp" #include "modes/profile_world.hpp" #include "modes/world.hpp" -#include "network/network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/network_world.hpp" +#include "online/http_manager.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" #include "utils/profiler.hpp" @@ -73,14 +76,14 @@ float MainLoop::getLimitedDt() // Throttle fps if more than maximum, which can reduce // the noise the fan on a graphics card makes. // When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus - const int max_fps = (StateManager::get()->throttleFPS() ? 35 : UserConfigParams::m_max_fps); + const int max_fps = 35;//(StateManager::get()->throttleFPS() ? 35 : UserConfigParams::m_max_fps); const int current_fps = (int)(1000.0f/dt); - if( current_fps > max_fps && !ProfileWorld::isNoGraphics()) + if( current_fps > max_fps && !ProfileWorld::isProfileMode()) { int wait_time = 1000/max_fps - 1000/current_fps; if(wait_time < 1) wait_time = 1; - irr_driver->getDevice()->sleep(wait_time); + StkTime::sleep(wait_time); } else break; } @@ -94,22 +97,12 @@ float MainLoop::getLimitedDt() */ void MainLoop::updateRace(float dt) { - // Server: Send the current position and previous controls to all clients - // Client: send current controls to server - // But don't do this if the race is in finish phase (otherwise - // messages can be mixed up in the race manager) - if(!World::getWorld()->isFinishPhase()) - network_manager->sendUpdates(); if(ProfileWorld::isProfileMode()) dt=1.0f/60.0f; - // Again, only receive updates if the race isn't over - once the - // race results are displayed (i.e. game is in finish phase) - // messages must be handled by the normal update of the network - // manager - if(!World::getWorld()->isFinishPhase()) - network_manager->receiveUpdates(); - - World::getWorld()->updateWorld(dt); + if (NetworkWorld::getInstance()->isRunning()) + NetworkWorld::getInstance()->update(dt); + else + World::getWorld()->updateWorld(dt); } // updateRace //----------------------------------------------------------------------------- @@ -127,13 +120,8 @@ void MainLoop::run() m_prev_time = m_curr_time; float dt = getLimitedDt(); - network_manager->update(dt); - if (World::getWorld()) // race is active if world exists { - // Busy wait if race_manager is active (i.e. creating of world is done) - // till all clients have reached this state. - if (network_manager->getState()==NetworkManager::NS_READY_SET_GO_BARRIER) continue; updateRace(dt); } // if race is active @@ -162,7 +150,28 @@ void MainLoop::run() PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); irr_driver->update(dt); PROFILER_POP_CPU_MARKER(); + + PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); + ProtocolManager::getInstance()->update(); + PROFILER_POP_CPU_MARKER(); + + PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); + Online::HTTPManager::get()->update(dt); + PROFILER_POP_CPU_MARKER(); + + PROFILER_SYNC_FRAME(); } + else if (!m_abort && ProfileWorld::isNoGraphics()) + { + PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); + ProtocolManager::getInstance()->update(); + PROFILER_POP_CPU_MARKER(); + + PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); + Online::HTTPManager::get()->update(dt); + PROFILER_POP_CPU_MARKER(); + } + PROFILER_SYNC_FRAME(); PROFILER_POP_CPU_MARKER(); } // while !m_exit diff --git a/src/main_loop.hpp b/src/main_loop.hpp index f2a376ad6..345e36c7a 100644 --- a/src/main_loop.hpp +++ b/src/main_loop.hpp @@ -1,6 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Ingo Ruhnke +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -39,6 +40,9 @@ public: ~MainLoop(); void run(); void abort(); + // ------------------------------------------------------------------------ + /** Returns true if STK is to be stoppe. */ + bool isAborted() const { return m_abort; } }; // MainLoop extern MainLoop* main_loop; diff --git a/src/modes/cutscene_world.cpp b/src/modes/cutscene_world.cpp index d0361ef8c..ea01df566 100644 --- a/src/modes/cutscene_world.cpp +++ b/src/modes/cutscene_world.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -35,7 +35,7 @@ #include "physics/physics.hpp" #include "states_screens/credits.hpp" #include "states_screens/cutscene_gui.hpp" -#include "states_screens/kart_selection.hpp" +#include "states_screens/offline_kart_selection.hpp" #include "states_screens/main_menu_screen.hpp" #include "tracks/track.hpp" #include "tracks/track_object.hpp" @@ -384,7 +384,7 @@ void CutsceneWorld::enterRaceOverState() slot->setFirstTime(false); unlock_manager->save(); - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); s->setMultiplayer(false); s->setGoToOverworldNext(); StateManager::get()->pushScreen( s ); diff --git a/src/modes/cutscene_world.hpp b/src/modes/cutscene_world.hpp index a1b5025ef..db76d182a 100644 --- a/src/modes/cutscene_world.hpp +++ b/src/modes/cutscene_world.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/demo_world.cpp b/src/modes/demo_world.cpp index 681382e7e..c78c1abd9 100644 --- a/src/modes/demo_world.cpp +++ b/src/modes/demo_world.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,7 +21,6 @@ #include "guiengine/modaldialog.hpp" #include "input/device_manager.hpp" #include "input/input_manager.hpp" -#include "network/network_manager.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" @@ -141,7 +140,7 @@ bool DemoWorld::updateIdleTimeAndStartDemo(float dt) // Use keyboard 0 by default in --no-start-screen device = input_manager->getDeviceList()->getKeyboard(0); StateManager::get()->createActivePlayer( - UserConfigParams::m_all_players.get(0), device ); + UserConfigParams::m_all_players.get(0), device , NULL); // ASSIGN should make sure that only input from assigned devices // is read. input_manager->getDeviceList()->setAssignMode(ASSIGN); @@ -149,7 +148,7 @@ bool DemoWorld::updateIdleTimeAndStartDemo(float dt) m_do_demo = true; race_manager->setNumKarts(m_num_karts); race_manager->setLocalKartInfo(0, "tux"); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startSingleRace(m_demo_tracks[0], m_num_laps, false); m_demo_tracks.push_back(m_demo_tracks[0]); m_demo_tracks.erase(m_demo_tracks.begin()); diff --git a/src/modes/demo_world.hpp b/src/modes/demo_world.hpp index 23f8d46bf..a31811e67 100644 --- a/src/modes/demo_world.hpp +++ b/src/modes/demo_world.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/easter_egg_hunt.cpp b/src/modes/easter_egg_hunt.cpp index 107df704d..4d44192cb 100644 --- a/src/modes/easter_egg_hunt.cpp +++ b/src/modes/easter_egg_hunt.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,7 +24,7 @@ //----------------------------------------------------------------------------- /** Constructor. Sets up the clock mode etc. */ -EasterEggHunt::EasterEggHunt() : WorldWithRank() +EasterEggHunt::EasterEggHunt() : LinearWorld() { WorldStatus::setClockMode(CLOCK_CHRONO); m_use_highscores = true; @@ -36,7 +36,7 @@ EasterEggHunt::EasterEggHunt() : WorldWithRank() */ void EasterEggHunt::init() { - WorldWithRank::init(); + LinearWorld::init(); m_display_rank = false; // check for possible problems if AI karts were incorrectly added @@ -160,8 +160,8 @@ void EasterEggHunt::collectedEasterEgg(const AbstractKart *kart) */ void EasterEggHunt::update(float dt) { - WorldWithRank::update(dt); - WorldWithRank::updateTrack(dt); + LinearWorld::update(dt); + LinearWorld::updateTrack(dt); } // update //----------------------------------------------------------------------------- @@ -181,7 +181,7 @@ bool EasterEggHunt::isRaceOver() */ void EasterEggHunt::reset() { - WorldWithRank::reset(); + LinearWorld::reset(); for(unsigned int i=0; igetControls().reset(); + WorldWithRank::terminateRace(); +} +//----------------------------------------------------------------------------- +/** In Easter Egg mode the finish time is just the time the race is over, + * since there are no AI karts. + */ +float EasterEggHunt::estimateFinishTimeForKart(AbstractKart* kart) +{ + return getTime(); +} // estimateFinishTimeForKart diff --git a/src/modes/easter_egg_hunt.hpp b/src/modes/easter_egg_hunt.hpp index 31ba96fb4..309bd1f5e 100755 --- a/src/modes/easter_egg_hunt.hpp +++ b/src/modes/easter_egg_hunt.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,7 +19,7 @@ #ifndef EASTER_EGG_HUNT_HPP #define EASTER_EGG_HUNT_HPP -#include "modes/world_with_rank.hpp" +#include "modes/linear_world.hpp" #include "states_screens/race_gui_base.hpp" #include @@ -31,7 +31,7 @@ class AbstractKart; * \brief An implementation of World to provide an easter egg hunt like mode * \ingroup modes */ -class EasterEggHunt: public WorldWithRank +class EasterEggHunt: public LinearWorld { private: /** Keeps track of how many eggs each kart has found. */ @@ -56,7 +56,7 @@ public: virtual bool raceHasLaps(){ return false; } virtual const std::string& getIdent() const; - + virtual void terminateRace(); virtual void update(float dt); virtual void getKartsDisplayInfo( std::vector *info); @@ -64,6 +64,10 @@ public: void updateKartRanks(); void collectedEasterEgg(const AbstractKart *kart); void readData(const std::string &filename); + + virtual void checkForWrongDirection(unsigned int i) OVERRIDE; + virtual float estimateFinishTimeForKart(AbstractKart* kart) OVERRIDE; + }; // EasterEggHunt diff --git a/src/modes/follow_the_leader.cpp b/src/modes/follow_the_leader.cpp index a81ff1dd3..a3a712d97 100644 --- a/src/modes/follow_the_leader.cpp +++ b/src/modes/follow_the_leader.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/follow_the_leader.hpp b/src/modes/follow_the_leader.hpp index 46dd5abd9..5af95c780 100644 --- a/src/modes/follow_the_leader.hpp +++ b/src/modes/follow_the_leader.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index c519703c1..b96d7ac98 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,7 +25,6 @@ #include "karts/abstract_kart.hpp" #include "karts/controller/controller.hpp" #include "karts/kart_properties.hpp" -#include "network/network_manager.hpp" #include "physics/physics.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" @@ -319,13 +318,7 @@ void LinearWorld::newLap(unsigned int kart_index) // Race finished if(kart_info.m_race_lap >= race_manager->getNumLaps() && raceHasLaps()) { - // A client does not detect race finished by itself, it will - // receive a message from the server. So a client does not do - // anything here. - if(network_manager->getMode()!=NetworkManager::NW_CLIENT) - { - kart->finishedRace(getTime()); - } + kart->finishedRace(getTime()); } float time_per_lap; if (kart_info.m_race_lap == 1) // just completed first lap diff --git a/src/modes/linear_world.hpp b/src/modes/linear_world.hpp index 15ed57ed7..e4dd3e6bd 100644 --- a/src/modes/linear_world.hpp +++ b/src/modes/linear_world.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/overworld.cpp b/src/modes/overworld.cpp index 65cdf8f10..00344b3c2 100644 --- a/src/modes/overworld.cpp +++ b/src/modes/overworld.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,9 +27,8 @@ #include "karts/rescue_animation.hpp" #include "modes/overworld.hpp" #include "physics/physics.hpp" -#include "network/network_manager.hpp" #include "states_screens/dialogs/select_challenge.hpp" -#include "states_screens/kart_selection.hpp" +#include "states_screens/offline_kart_selection.hpp" #include "states_screens/race_gui_overworld.hpp" #include "tracks/track.hpp" @@ -64,7 +63,7 @@ void OverWorld::enterOverWorld() // Create player and associate player with keyboard StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + device, NULL); if (!kart_properties_manager->getKart(UserConfigParams::m_default_kart)) { @@ -83,13 +82,13 @@ void OverWorld::enterOverWorld() ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); if(race_manager->haveKartLastPositionOnOverworld()){ - OverWorld *ow = (OverWorld*)World::getWorld(); - ow->getKart(0)->setXYZ(race_manager->getKartLastPositionOnOverworld()); - ow->moveKartAfterRescue(ow->getKart(0)); - } + OverWorld *ow = (OverWorld*)World::getWorld(); + ow->getKart(0)->setXYZ(race_manager->getKartLastPositionOnOverworld()); + ow->moveKartAfterRescue(ow->getKart(0)); + } irr_driver->showPointer(); // User should be able to click on the minimap } // enterOverWorld @@ -116,6 +115,7 @@ void OverWorld::update(float dt) m_karts[0]->startEngineSFX(); } WorldWithRank::update(dt); + WorldWithRank::updateTrack(dt); const unsigned int kart_amount = m_karts.size(); // isn't it cool, on the overworld nitro is free! @@ -129,7 +129,7 @@ void OverWorld::update(float dt) m_return_to_garage = false; delayedSelfDestruct(); race_manager->exitRace(false); - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); s->setMultiplayer(false); s->setFromOverworld(true); StateManager::get()->resetAndGoToScreen(s); diff --git a/src/modes/overworld.hpp b/src/modes/overworld.hpp index a1dbdb752..569a8d515 100644 --- a/src/modes/overworld.hpp +++ b/src/modes/overworld.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/profile_world.cpp b/src/modes/profile_world.cpp index f8a632fe2..b6bb5d109 100644 --- a/src/modes/profile_world.cpp +++ b/src/modes/profile_world.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/profile_world.hpp b/src/modes/profile_world.hpp index bc05260d9..f7c7b4ad2 100644 --- a/src/modes/profile_world.hpp +++ b/src/modes/profile_world.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index b268391f8..e17e88315 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -41,11 +41,23 @@ */ SoccerWorld::SoccerWorld() : WorldWithRank() { - WorldStatus::setClockMode(CLOCK_CHRONO); + if(race_manager->hasTimeTarget()){ + WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN, race_manager->getTimeTarget()); + countDownReachedZero = false; + } + else WorldStatus::setClockMode(CLOCK_CHRONO); m_use_highscores = false; } // SoccerWorld - //----------------------------------------------------------------------------- + +/** The destructor frees al data structures. + */ +SoccerWorld::~SoccerWorld() +{ + sfx_manager->deleteSFX(m_goal_sound); +} // ~SoccerWorld +//----------------------------------------------------------------------------- + /** Initializes the soccer world. It sets up the data structure * to keep track of points etc. for each kart. */ @@ -55,7 +67,7 @@ void SoccerWorld::init() m_display_rank = false; m_goal_timer = 0.f; m_lastKartToHitBall = -1; - + // check for possible problems if AI karts were incorrectly added if(getNumKarts() > race_manager->getNumPlayers()) { @@ -73,6 +85,11 @@ void SoccerWorld::init() void SoccerWorld::reset() { WorldWithRank::reset(); + if(race_manager->hasTimeTarget()){ + WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN, race_manager->getTimeTarget()); + countDownReachedZero = false; + } + else WorldStatus::setClockMode(CLOCK_CHRONO); m_can_score_points = true; memset(m_team_goals, 0, sizeof(m_team_goals)); @@ -95,6 +112,12 @@ void SoccerWorld::reset() obj->reset(); obj->getPhysics()->reset(); } + + if (m_goal_sound != NULL && + m_goal_sound->getStatus() == SFXManager::SFX_PLAYING) + { + m_goal_sound->stop(); + } initKartList(); } // reset @@ -151,11 +174,17 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) { if(first_goal){ m_redScorers.push_back(m_lastKartToHitBall); - m_redScoreTimes.push_back(world->getTime()); + if(race_manager->hasTimeTarget()) + m_redScoreTimes.push_back(race_manager->getTimeTarget() - world->getTime()); + else + m_redScoreTimes.push_back(world->getTime()); } else{ m_blueScorers.push_back(m_lastKartToHitBall); - m_blueScoreTimes.push_back(world->getTime()); + if(race_manager->hasTimeTarget()) + m_blueScoreTimes.push_back(race_manager->getTimeTarget() - world->getTime()); + else + m_blueScoreTimes.push_back(world->getTime()); } } } @@ -210,7 +239,11 @@ bool SoccerWorld::isRaceOver() { return false; } - // One team scored the target goals ... + + else if(race_manager->hasTimeTarget()){ + return countDownReachedZero; + } + // One team scored the target goals ... else if(getScore(0) >= m_goal_target || getScore(1) >= m_goal_target ) { @@ -230,6 +263,12 @@ void SoccerWorld::terminateRace() m_can_score_points = false; WorldWithRank::terminateRace(); } // terminateRace +//----------------------------------------------------------------------------- +/** Called when the match time ends. +*/ +void SoccerWorld::countdownReachedZero(){ + countDownReachedZero = true; +} //----------------------------------------------------------------------------- /** Returns the data to display in the race gui. @@ -413,7 +452,7 @@ void SoccerWorld::initKartList() // Set kart positions, ordering them by team for(unsigned int n=0; ngetLocalKartInfo(n).getSoccerTeam(); m_karts[n]->setPosition(team_cur_position[team]); team_cur_position[team]++; @@ -428,12 +467,14 @@ int SoccerWorld::getScore(unsigned int i) //----------------------------------------------------------------------------- int SoccerWorld::getTeamLeader(unsigned int team) { - for(unsigned int i = 0; i< m_karts.size(); i++){ - if(race_manager->getLocalKartInfo(i).getSoccerTeam() == (SoccerTeam) team) - return i; - } + for(unsigned int i = 0; i< m_karts.size(); i++) + { + if(race_manager->getLocalKartInfo(i).getSoccerTeam() == (SoccerTeam) team) + return i; + } return -1; - } +} // getTeamLeader + //----------------------------------------------------------------------------- AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, int local_player_id, int global_player_id, diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index ef922610a..d2778c599 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -45,27 +45,29 @@ private: int m_team_goals[NB_SOCCER_TEAMS]; /** Number of goals needed to win */ int m_goal_target; + bool countDownReachedZero; /** Whether or not goals can be scored (they are disabled when a point is scored and re-enabled when the next game can be played)*/ bool m_can_score_points; SFXBase *m_goal_sound; /** Timer for displaying goal text*/ float m_goal_timer; - int m_lastKartToHitBall; - std::vector m_redScorers; - std::vector m_redScoreTimes; - std::vector m_blueScorers; - std::vector m_blueScoreTimes; + int m_lastKartToHitBall; + std::vector m_redScorers; + std::vector m_redScoreTimes; + std::vector m_blueScorers; + std::vector m_blueScoreTimes; public: SoccerWorld(); - virtual ~SoccerWorld() {} + virtual ~SoccerWorld(); virtual void init(); // clock events virtual bool isRaceOver(); virtual void terminateRace(); + virtual void countdownReachedZero() OVERRIDE; // overriding World methods virtual void reset(); @@ -83,21 +85,21 @@ public: void onCheckGoalTriggered(bool first_goal); int getTeamLeader(unsigned int i); - void setLastKartTohitBall(unsigned int kartId); - std::vector getScorers(unsigned int team) + void setLastKartTohitBall(unsigned int kartId); + std::vector getScorers(unsigned int team) { - if(team == 0) - return m_redScorers; + if(team == 0) + return m_redScorers; else - return m_blueScorers; - } - std::vector getScoreTimes(unsigned int team) + return m_blueScorers; + } + std::vector getScoreTimes(unsigned int team) { - if(team == 0) - return m_redScoreTimes; - else - return m_blueScoreTimes; - } + if(team == 0) + return m_redScoreTimes; + else + return m_blueScoreTimes; + } private: void initKartList(); diff --git a/src/modes/standard_race.cpp b/src/modes/standard_race.cpp index 1571992bf..f6102db9e 100644 --- a/src/modes/standard_race.cpp +++ b/src/modes/standard_race.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,6 +21,7 @@ #include "items/powerup_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/controller/controller.hpp" +#include "network/network_manager.hpp" //----------------------------------------------------------------------------- StandardRace::StandardRace() : LinearWorld() @@ -123,5 +124,6 @@ void StandardRace::endRaceEarly() } // Finish the active players endSetKartPositions(); setPhase(RESULT_DISPLAY_PHASE); - terminateRace(); + if (!isNetworkWorld() || NetworkManager::getInstance()->isServer()) + terminateRace(); } // endRaceEarly diff --git a/src/modes/standard_race.hpp b/src/modes/standard_race.hpp index b37f24a7d..ddfc6efc5 100644 --- a/src/modes/standard_race.hpp +++ b/src/modes/standard_race.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 19255b2c6..edb3a1ab3 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -1,7 +1,7 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -300,17 +300,15 @@ void ThreeStrikesBattle::update(float dt) WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); - updateKartNodes(); - - core::vector3df tire_offset; - std::string tire; - float scale = 0.5f; - float radius = 0.5f; - PhysicalObject::bodyTypes body_shape; - - // insert blown away tire(s) now if was requested + PhysicalObject::BodyTypes body_shape; + updateKartNodes(); // insert blown away tire(s) now if was requested while (m_insert_tire > 0) { + std::string tire; + core::vector3df tire_offset; + float scale = 0.5f; + float radius = 0.5f; + PhysicalObject::BodyTypes body_shape; if(m_insert_tire == 1) { tire_offset = core::vector3df(0.0f, 0.0f, 0.0f); @@ -338,35 +336,31 @@ void ThreeStrikesBattle::update(float dt) core::vector3df tire_xyz = m_tire_position + tire_offset; core::vector3df tire_hpr = core::vector3df(800.0f,0, - m_tire_rotation / M_PI * 180 + 180); + m_tire_rotation *RAD_TO_DEGREE + 180); core::vector3df tire_scale(scale,scale,scale); - PhysicalObject::Settings physicsSettings; - physicsSettings.body_type = PhysicalObject::MP_CYLINDER_Y; - physicsSettings.crash_reset = false; - physicsSettings.knock_kart = false; - physicsSettings.mass = 15.0f; - physicsSettings.radius = radius; - physicsSettings.reset_when_too_low = false; + PhysicalObject::Settings physics_settings(body_shape, + radius, /*mass*/15.0f); TrackObjectPresentationMesh* tire_presentation = new TrackObjectPresentationMesh(tire, tire_xyz, tire_hpr, tire_scale); - TrackObject* tire = new TrackObject(tire_xyz, tire_hpr, tire_scale, - "movable", tire_presentation, - true /* is_dynamic */, - &physicsSettings); - getTrack()->getTrackObjectManager()->insertObject(tire); + TrackObject* tire_obj = new TrackObject(tire_xyz, tire_hpr, tire_scale, + "movable", tire_presentation, + true /* is_dynamic */, + &physics_settings); + getTrack()->getTrackObjectManager()->insertObject(tire_obj); // FIXME: orient the force relative to kart orientation - tire->getPhysics()->getBody()->applyCentralForce(btVector3(60.0f, 0.0f, 0.0f)); + tire_obj->getPhysics()->getBody() + ->applyCentralForce(btVector3(60.0f, 0.0f, 0.0f)); m_insert_tire--; if(m_insert_tire == 1) m_insert_tire = 0; - m_tires.push_back(tire); - } + m_tires.push_back(tire_obj); + } // while } // update //----------------------------------------------------------------------------- diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index dbe2a57a9..00afb656e 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/tutorial_world.cpp b/src/modes/tutorial_world.cpp index 70ddabad8..d94d154a0 100644 --- a/src/modes/tutorial_world.cpp +++ b/src/modes/tutorial_world.cpp @@ -1,3 +1,21 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2012-2013 Marianne Gagnon +// +// 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/tutorial_world.hpp" #include "karts/kart.hpp" diff --git a/src/modes/tutorial_world.hpp b/src/modes/tutorial_world.hpp index 5c2b3b2a1..e18adc6b4 100644 --- a/src/modes/tutorial_world.hpp +++ b/src/modes/tutorial_world.hpp @@ -1,3 +1,21 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2012-2013 Marianne Gagnon +// +// 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 HEADER_TUTORIAL_MODE_HPP #define HEADER_TUTORIAL_MODE_HPP diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 092b48d99..ca1d3668e 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,6 +24,7 @@ #include #include +#include "achievements/achievements_manager.hpp" #include "audio/music_manager.hpp" #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" @@ -39,12 +40,11 @@ #include "karts/controller/player_controller.hpp" #include "karts/controller/end_controller.hpp" #include "karts/controller/skidding_ai.hpp" +#include "karts/controller/network_player_controller.hpp" #include "karts/kart.hpp" #include "karts/kart_properties_manager.hpp" #include "modes/overworld.hpp" #include "modes/profile_world.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" #include "physics/btKart.hpp" #include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" @@ -80,8 +80,8 @@ World* World::m_world = NULL; * of all karts is set (i.e. in a normal race the arrival time for karts * will be estimated), highscore is updated, and the race result gui * is being displayed. - * Rescuing is handled via the three functions: - * getNumberOfRescuePositions() - which returns the number of rescue + * Rescuing is handled via the three functions: + * getNumberOfRescuePositions() - which returns the number of rescue * positions defined. * getRescuePositionIndex(AbstractKart *kart) - which determines the * index of the rescue position to be used for the given kart. @@ -119,6 +119,7 @@ World::World() : WorldStatus(), m_clear_color(255,100,101,140) m_schedule_exit_race = false; m_self_destruct = false; m_schedule_tutorial = false; + m_is_network_world = false; m_stop_music_when_dialog_open = true; @@ -134,7 +135,6 @@ World::World() : WorldStatus(), m_clear_color(255,100,101,140) */ void World::init() { - race_state = new RaceState(); m_faster_music_active = false; m_fastest_kart = 0; m_eliminated_karts = 0; @@ -184,14 +184,15 @@ void World::init() } // for i + // Now that all models are loaded, apply the overrides + irr_driver->applyObjectPassShader(); + // Must be called after all karts are created m_race_gui->init(); if(ReplayPlay::get()) ReplayPlay::get()->Load(); - network_manager->worldLoaded(); - powerup_manager->updateWeightsForRace(num_karts); } // init @@ -219,6 +220,7 @@ void World::reset() m_faster_music_active = false; m_eliminated_karts = 0; m_eliminated_players = 0; + m_is_network_world = false; for ( KartList::iterator i = m_karts.begin(); i != m_karts.end() ; ++i ) { @@ -266,11 +268,7 @@ void World::reset() void World::createRaceGUI() { - //if(UserConfigParams::m_minimal_race_gui && - // race_manager->getTrackName() != "tutorial") - // m_race_gui = new MinimalRaceGUI(); - //else - m_race_gui = new RaceGUI(); + m_race_gui = new RaceGUI(); } //----------------------------------------------------------------------------- @@ -301,11 +299,10 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, m_num_players ++; break; case RaceManager::KT_NETWORK_PLAYER: - break; // Avoid compiler warning about enum not handled. - //controller = new NetworkController(kart_ident, position, init_pos, - // global_player_id); - //m_num_players++; - //break; + controller = new NetworkPlayerController(new_kart, + StateManager::get()->getActivePlayer(local_player_id)); + m_num_players++; + break; case RaceManager::KT_AI: controller = loadAIController(new_kart); break; @@ -382,7 +379,6 @@ World::~World() // gui and this must be deleted. delete m_race_gui; } - delete race_state; for ( unsigned int i = 0 ; i < m_karts.size() ; i++ ) delete m_karts[i]; @@ -448,10 +444,16 @@ void World::terminateRace() int best_finish_time = -1; std::string highscore_who = ""; StateManager::ActivePlayer* best_player = NULL; - updateHighscores(&best_highscore_rank, &best_finish_time, &highscore_who, + if (!this->isNetworkWorld()) + { + updateHighscores(&best_highscore_rank, &best_finish_time, &highscore_who, &best_player); + unlock_manager->getCurrentSlot()->raceFinished(); + } unlock_manager->getCurrentSlot()->raceFinished(); + ((MapAchievement *) AchievementsManager::get()->getActive()->getAchievement(1))->increase(getTrack()->getIdent(), 1); + AchievementsManager::get()->onRaceEnd(); if (m_race_gui) m_race_gui->clearAllMessages(); // we can't delete the race gui here, since it is needed in case of @@ -498,7 +500,7 @@ void World::resetAllKarts() // Loop over all karts, in case that some karts are dfferent for(unsigned int kart_id=0; kart_idsetSuspensionLength(); // Update the kart transforms with the newly computed position // after all karts are reset (*i)->setTrans((*i)->getBody()->getWorldTransform()); @@ -657,7 +656,7 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform) kart->getBody()->setCenterOfMassTransform(pos); // Project kart to surface of track - // This will set the physics transform + // This will set the physics transform m_track->findGround(kart); } // moveKartTo @@ -756,13 +755,12 @@ void World::updateWorld(float dt) InputDevice* device = input_manager->getDeviceList()->getKeyboard(0); // Create player and associate player with keyboard - StateManager::get() - ->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), + device, NULL); if (!kart_properties_manager->getKart(UserConfigParams::m_default_kart)) { - Log::warn("World", + Log::warn("World", "Cannot find kart '%s', will revert to default.", UserConfigParams::m_default_kart.c_str()); UserConfigParams::m_default_kart.revertToDefaults(); @@ -776,7 +774,7 @@ void World::updateWorld(float dt) ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(true); } else @@ -829,11 +827,8 @@ void World::update(float dt) if(ReplayPlay::get()) ReplayPlay::get()->update(dt); if(history->replayHistory()) dt=history->getNextDelta(); WorldStatus::update(dt); - // Clear race state so that new information can be stored - race_state->clear(); - if(network_manager->getMode()!=NetworkManager::NW_CLIENT && - !history->dontDoPhysics()) + if (!history->dontDoPhysics()) { m_physics->update(dt); } @@ -964,7 +959,9 @@ void World::updateHighscores(int* best_highscore_rank, int* best_finish_time, PlayerController *controller = (PlayerController*)(k->getController()); - int highscore_rank = highscores->addData(k->getIdent(), + int highscore_rank = 0; + if (controller->getPlayer()->getProfile() != NULL) // if we have the player profile here + highscore_rank = highscores->addData(k->getIdent(), controller->getPlayer()->getProfile()->getName(), k->getFinishTime()); @@ -996,7 +993,8 @@ AbstractKart *World::getPlayerKart(unsigned int n) const unsigned int count=-1; for(unsigned int i=0; igetController()->isPlayerController()) + if(m_karts[i]->getController()->isPlayerController() || + m_karts[i]->getController()->isNetworkController()) { count++; if(count==n) return m_karts[i]; diff --git a/src/modes/world.hpp b/src/modes/world.hpp index c981c960c..a1d41d83d 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -152,6 +152,9 @@ protected: */ bool m_self_destruct; + /** Set when the world is online and counts network players. */ + bool m_is_network_world; + virtual void onGo(); /** Returns true if the race is over. Must be defined by all modes. */ virtual bool isRaceOver() = 0; @@ -197,7 +200,7 @@ public: * this method in child classes to provide it. */ virtual const std::string& getIdent() const = 0; // ------------------------------------------------------------------------ - /** Returns the number of rescue positions on a given track and game + /** Returns the number of rescue positions on a given track and game * mode. */ virtual unsigned int getNumberOfRescuePositions() const OVERRIDE = 0; // ------------------------------------------------------------------------ @@ -282,6 +285,9 @@ public: assert(kartId >= 0 && kartId < int(m_karts.size())); return m_karts[kartId]; } // ------------------------------------------------------------------------ + /** Returns all karts. */ + const KartList & getKarts() const { return m_karts; } + // ------------------------------------------------------------------------ /** Returns the number of currently active (i.e.non-elikminated) karts. */ unsigned int getCurrentNumKarts() const { return (int)m_karts.size() - m_eliminated_karts; } @@ -324,6 +330,10 @@ public: // ------------------------------------------------------------------------ virtual void escapePressed(); + /** Set the network mode (true if networked) */ + void setNetworkWorld(bool is_networked) { m_is_network_world = is_networked; } + + bool isNetworkWorld() const { return m_is_network_world; } }; // World #endif diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 174df9da3..440903230 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -26,7 +26,6 @@ #include "karts/abstract_kart.hpp" #include "modes/world.hpp" #include "tracks/track.hpp" -#include "network/network_manager.hpp" #include @@ -112,8 +111,6 @@ void WorldStatus::enterRaceOverState() */ void WorldStatus::terminateRace() { - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->sendRaceResults(); } // terminateRace //----------------------------------------------------------------------------- @@ -146,7 +143,7 @@ void WorldStatus::update(const float dt) // long, we use the aux timer to force the next phase // after 3.5 seconds. if(m_track_intro_sound->getStatus()==SFXManager::SFX_PLAYING - && m_auxiliary_timer<3.5f) + && m_auxiliary_timer<3.5f) return; // Wait before ready phase if sounds are disabled if(!UserConfigParams::m_sfx && m_auxiliary_timer<3.0f) @@ -239,8 +236,8 @@ void WorldStatus::update(const float dt) case FINISH_PHASE: // Nothing to do here. break; - case GOAL_PHASE: - // Nothing to do here as well. + case GOAL_PHASE: + // Nothing to do here as well. default: break; } diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index 421123205..bfdf30aa9 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -79,8 +79,8 @@ public: // Undefined, used in asserts to catch incorrect states. UNDEFINED_PHASE, - //Goal scored phase - GOAL_PHASE + //Goal scored phase + GOAL_PHASE }; protected: /** Sound to play at the beginning of a race, during which a diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index e4668ef41..7db5f6047 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/modes/world_with_rank.hpp b/src/modes/world_with_rank.hpp index 887ca1028..100802d7c 100644 --- a/src/modes/world_with_rank.hpp +++ b/src/modes/world_with_rank.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/network/character_confirm_message.hpp b/src/network/character_confirm_message.hpp deleted file mode 100644 index 527794928..000000000 --- a/src/network/character_confirm_message.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 HEADER_CHARACTER_CONFIRM_MESSAGE_HPP -#define HEADER_CHARACTER_CONFIRM_MESSAGE_HPP - -#include - -#include "network/message.hpp" - - -/** This message is from the server to all clients to inform them about a - * newly selected character. This means that this character is not available - * anymore. The message contains the hostid of the client who selected this - * character (0 in case of server), so that this message acts as a - * confirmation for the corresponding client (or a reject if the message has - * a different hostid, meaning that another client selected the character - * earlier). - */ -class CharacterConfirmMessage : public Message -{ -private: - /** The host id. */ - int m_host_id; - /** Name of the selected kart. */ - std::string m_kart_name; -public: - /** Constructor, takes the name of the kart name and the host id. - * \param kart_name Name of the kart. - * \param host_id Id of the host who selected this character. - */ - CharacterConfirmMessage(const std::string &kart_name, int host_id) - : Message(Message::MT_CHARACTER_CONFIRM) - { - allocate(getStringLength(kart_name) + getCharLength()); - addString(kart_name); - addChar(host_id); - } // CharacterConfirmMessage - - // ------------------------------------------------------------------------ - /** Unpacks a character confirm message. - * \param pkt Received enet packet. - */ - CharacterConfirmMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_CONFIRM) - { - m_kart_name = getString(); - m_host_id = getChar(); - } // CharacterConfirmMessage(EnetPacket) - // ------------------------------------------------------------------------ - /** Returns the kart name contained in a received message. */ - const std::string &getKartName() const { return m_kart_name; } - /** Returns the host id contained in a received message. */ - int getHostId() const { return m_host_id; } - -}; // CharacterConfirmMessage -#endif diff --git a/src/network/character_info_message.hpp b/src/network/character_info_message.hpp deleted file mode 100644 index 443a2d670..000000000 --- a/src/network/character_info_message.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 HEADER_CHARACTER_INFO_MESSAGE_HPP -#define HEADER_CHARACTER_INFO_MESSAGE_HPP - -#include "karts/kart_properties_manager.hpp" -#include "network/message.hpp" - -/** This message is sent from the server to the clients and contains the list - * of available characters. Additionally, it contains the clients id. - */ -class CharacterInfoMessage : public Message -{ -// Add the remote host id to this message (to avoid sending this separately) -public: - CharacterInfoMessage(int hostid) : Message(Message::MT_CHARACTER_INFO) - { - std::vector all_karts = - kart_properties_manager->getAllAvailableKarts(); - allocate(getCharLength()+getStringVectorLength(all_karts)); - addChar(hostid); - addStringVector(all_karts); - } - // ------------------------------------------------------------------------ - CharacterInfoMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_INFO) - { - int hostid=getChar(); - network_manager->setHostId(hostid); - std::vector all_karts; - all_karts = getStringVector(); - kart_properties_manager->setUnavailableKarts(all_karts); - } -}; // CharacterInfoMessage -#endif diff --git a/src/network/character_selected_message.hpp b/src/network/character_selected_message.hpp deleted file mode 100644 index 9e8a61961..000000000 --- a/src/network/character_selected_message.hpp +++ /dev/null @@ -1,105 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 HEADER_CHARACTER_SELECTED_MESSAGE_HPP -#define HEADER_CHARACTER_SELECTED_MESSAGE_HPP - -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" -#include "race/race_manager.hpp" - -/** This message is send contains information about selected karts. It is send - * from the client to the server to indicate a selected kart, and from the - * server to the clients to indicate that a kart was selected. In the latter - * case it contains the hostid of the successful selecter. This way a client - * selecting a kart can check if its selection was successful or not, and - * other clients are informed that a certain kart is not available anymore. - */ -class CharacterSelectedMessage : public Message -{ -private: - /** Number of local players on a host. If the message is send from the - * server to the clients, this field instead contains the host id of - * the host which selected the kart - */ - int m_num_local_players; - /** Stores information about the selected kart. */ - RemoteKartInfo m_kart_info; - -public: - /** Contains information about a selected kart. When send from the client - * to the server, it contains the number of local players (which - * technically needs only to be sent once); when send from from the server - * to the clients this field instead contains the host id of the host - * selected the character. This allows the client to detect if a selected - * kart was not confirmed by the server (i.e. another client or the server - * has selected the kart first - * \param player_id The local player id. - * \param host_id If this value is specified (>-1), then this value is - * used in the message instead of the number of local - * players. - */ - CharacterSelectedMessage(int player_id, int host_id=-1) - : Message(Message::MT_CHARACTER_INFO) - { - m_kart_info = race_manager->getLocalKartInfo(player_id); - m_num_local_players = race_manager->getNumLocalPlayers(); - - allocate(getCharLength() // m_kart_info.getLocalPlayerId()) - +getStringLength(m_kart_info.getKartName()) - +m_kart_info.getPlayerName().size() + 1 // FIXME: encoding issues - +getCharLength()); // m_num_local_players) - addChar(m_kart_info.getLocalPlayerId()); - addString(m_kart_info.getKartName()); - addString(core::stringc(m_kart_info.getPlayerName().c_str()).c_str()); // FIXME: encoding issues - // Piggy backing this information saves sending it as a separate - // message. It is actually only required in the first message - if(host_id>-1) - addChar(host_id); - else - addChar(race_manager->getNumLocalPlayers()); - } // CharacterSelectedMessage - - // ------------------------------------------------------------------------ - /** Unpacks a character selected message. The additional field is either - * the number of local players (when send from client to server), or the - * hostid of the host selected the character. - * \param pkt Received enet packet. - */ - CharacterSelectedMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_INFO) - { - m_kart_info.setLocalPlayerId(getChar()); - m_kart_info.setKartName(getString()); - m_kart_info.setPlayerName(core::stringw(getString().c_str())); // FIXME: encoding issues - m_num_local_players = getChar(); - } // CharacterSelectedMessage(EnetPacket) - - // ------------------------------------------------------------------------ - /** Returns the remote kart info structure of the selected kart. */ - const RemoteKartInfo& getKartInfo () const { return m_kart_info; } - - /** Returns the number of local players. */ - int getNumPlayers() const { return m_num_local_players; } - - /** Returns the host id of the host who selected the kart successfully. - * This information is actually stored in m_num_local_players field, which - * is used when a client receives this message. - */ - int getHostId () const { return m_num_local_players; } -}; // CharacterSelectedMessage -#endif diff --git a/src/network/client_network_manager.cpp b/src/network/client_network_manager.cpp new file mode 100644 index 000000000..dbd8e34b5 --- /dev/null +++ b/src/network/client_network_manager.cpp @@ -0,0 +1,166 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/client_network_manager.hpp" + +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" +#include "network/protocols/synchronization_protocol.hpp" + +#include "utils/log.hpp" + +#include +#include +#include + +void* waitInput(void* data) +{ + std::string str = ""; + bool stop = false; + + while(!stop) + { + getline(std::cin, str); + if (str == "quit") + { + stop = true; + } + else if (str == "select") + { + std::string str2; + getline(std::cin, str2); + Protocol* protocol = ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM); + ClientLobbyRoomProtocol* clrp = static_cast(protocol); + clrp->requestKartSelection(str2); + } + else if (str == "vote") + { + std::cout << "Vote for ? (track/laps/reversed/major/minor/race#) :"; + std::string str2; + getline(std::cin, str2); + Protocol* protocol = ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM); + ClientLobbyRoomProtocol* clrp = static_cast(protocol); + if (str2 == "track") + { + std::cin >> str2; + clrp->voteTrack(str2); + } + else if (str2 == "laps") + { + int cnt; + std::cin >> cnt; + clrp->voteLaps(cnt); + } + else if (str2 == "reversed") + { + bool cnt; + std::cin >> cnt; + clrp->voteReversed(cnt); + } + else if (str2 == "major") + { + int cnt; + std::cin >> cnt; + clrp->voteMajor(cnt); + } + else if (str2 == "minor") + { + int cnt; + std::cin >> cnt; + clrp->voteMinor(cnt); + } + else if (str2 == "race#") + { + int cnt; + std::cin >> cnt; + clrp->voteRaceCount(cnt); + } + } + else if (NetworkManager::getInstance()->getPeers().size() > 0) + { + NetworkString msg; + msg.ai8(0); + msg += str; + NetworkManager::getInstance()->getPeers()[0]->sendPacket(msg); + } + } + + exit(0); + + return NULL; +} + +ClientNetworkManager::ClientNetworkManager() +{ + m_thread_keyboard = NULL; + m_connected = false; +} + +ClientNetworkManager::~ClientNetworkManager() +{ + pthread_cancel(*m_thread_keyboard); +} + +void ClientNetworkManager::run() +{ + if (enet_initialize() != 0) + { + Log::error("ClientNetworkManager", "Could not initialize enet.\n"); + return; + } + m_localhost = new STKHost(); + m_localhost->setupClient(1, 2, 0, 0); + m_localhost->startListening(); + + Log::info("ClientNetworkManager", "Host initialized."); + + // listen keyboard console input + m_thread_keyboard = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_thread_keyboard, NULL, waitInput, NULL); + + NetworkManager::run(); + + Log::info("ClientNetworkManager", "Ready !"); +} + +void ClientNetworkManager::reset() +{ + NetworkManager::reset(); + + m_connected = false; + m_localhost = new STKHost(); + m_localhost->setupClient(1, 2, 0, 0); + m_localhost->startListening(); + +} + +void ClientNetworkManager::sendPacket(const NetworkString& data, bool reliable) +{ + if (m_peers.size() > 1) + Log::warn("ClientNetworkManager", "Ambiguous send of data.\n"); + m_peers[0]->sendPacket(data, reliable); +} + +STKPeer* ClientNetworkManager::getPeer() +{ + return m_peers[0]; +} diff --git a/src/network/client_network_manager.hpp b/src/network/client_network_manager.hpp new file mode 100644 index 000000000..422d08dc0 --- /dev/null +++ b/src/network/client_network_manager.hpp @@ -0,0 +1,81 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file client_network_manager.hpp + * \brief Defines a Client Network manager, that will connect to a server. + */ + +#ifndef CLIENT_NETWORK_MANAGER_HPP +#define CLIENT_NETWORK_MANAGER_HPP + +#include "network/network_manager.hpp" + +/*! \class ClientNetworkManager + * \ingroup network + */ +class ClientNetworkManager : public NetworkManager +{ + friend class Singleton; + public: + /*! \brief Get the instance. + * This is a utility function to avoid passing templates parameters + * to the getInstance singleton method. + */ + static ClientNetworkManager* getInstance() + { + return Singleton::getInstance(); + } + + /*! \brief Initializes network. + * This starts the threads and initializes network libraries. + */ + virtual void run(); + /*! \brief Resets the network socket. */ + virtual void reset(); + /*! \brief Sends a packet to the server. + * \param data : The network 8-bit string to send. + * \param reliable : If set to true, ENet will ensure that the packet is received. + */ + virtual void sendPacket(const NetworkString& data, bool reliable = true); + + /*! \brief Get the peer (the server) + * \return The peer with whom we're connected (if it exists). NULL elseway. + */ + STKPeer* getPeer(); + /*! \brief Function to know if we're a server. + * \return Returns true if we're on a server. False if we're a client. + */ + virtual bool isServer() { return false; } + /*! \brief Function used to notice the manager that we're connected to a server. + * \param value : True if we're connected, false elseway. + */ + void setConnected(bool value) { m_connected = value; } + /*! \brief Function to know if we're a server. + * \return Returns true if we're on a server. False if we're a client. + */ + bool isConnected() { return m_connected; } + + protected: + ClientNetworkManager(); + virtual ~ClientNetworkManager(); + + bool m_connected; //!< Is the user connected to a server + pthread_t* m_thread_keyboard; //!< The thread listening for keyboard console input. +}; + +#endif // CLIENT_NETWORK_MANAGER_HPP diff --git a/src/network/connect_message.cpp b/src/network/connect_message.cpp deleted file mode 100644 index 03660c158..000000000 --- a/src/network/connect_message.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 "network/connect_message.hpp" - -#include -#include -#include -#ifndef WIN32 -# include -#endif - -#include "config/player.hpp" -#include "karts/kart_properties_manager.hpp" -#include "states_screens/state_manager.hpp" -#include "tracks/track_manager.hpp" - -// ---------------------------------------------------------------------------- -/** Creates the connect message. It includes the id of the client (currently - * player name @ hostname), and the list of available tracks. - */ -ConnectMessage::ConnectMessage() : Message(MT_CONNECT) -{ - setId(); - const std::vector &all_tracks = - track_manager->getAllTrackIdentifiers(); - std::vector all_karts = - kart_properties_manager->getAllAvailableKarts(); - allocate(getStringLength(m_id) + getStringVectorLength(all_tracks) - + getStringVectorLength(all_karts)); - addString(m_id); - addStringVector(all_tracks); - addStringVector(all_karts); -} // ConnectMessage - -// ---------------------------------------------------------------------------- -/** Unpacks a connect message. The id of the client is stored in this object, - * and the list of tracks is used to set tracks that are not available on - * the client to be 'unavailable' on the server. - * \param pkt Enet packet. - */ -ConnectMessage::ConnectMessage(ENetPacket* pkt):Message(pkt, MT_CONNECT) -{ - m_id = getString(); - std::vector all_tracks = getStringVector(); - std::vector all_karts = getStringVector(); - track_manager->setUnavailableTracks(all_tracks); - kart_properties_manager->setUnavailableKarts(all_karts); -} // ConnectMessage - -// ---------------------------------------------------------------------------- -/** Sets the id, i.e. player name @ hostname, of this client. - */ -void ConnectMessage::setId() -{ - char hostname[256]; - gethostname(hostname, 255); - const std::string& id = core::stringc(StateManager::get()->getActivePlayerProfile(0)->getName()).c_str(); - std::ostringstream o; - o << id << '@' << hostname; - m_id = o.str(); -} // ConnectMessage diff --git a/src/network/event.cpp b/src/network/event.cpp new file mode 100644 index 000000000..1be77e397 --- /dev/null +++ b/src/network/event.cpp @@ -0,0 +1,96 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/event.hpp" +#include "network/network_manager.hpp" + +#include "utils/log.hpp" + +#include + +Event::Event(ENetEvent* event) +{ + switch (event->type) + { + case ENET_EVENT_TYPE_CONNECT: + type = EVENT_TYPE_CONNECTED; + break; + case ENET_EVENT_TYPE_DISCONNECT: + type = EVENT_TYPE_DISCONNECTED; + break; + case ENET_EVENT_TYPE_RECEIVE: + type = EVENT_TYPE_MESSAGE; + break; + case ENET_EVENT_TYPE_NONE: + return; + break; + } + if (type == EVENT_TYPE_MESSAGE) + { + m_data = NetworkString(std::string((char*)(event->packet->data), event->packet->dataLength-1)); + } + + m_packet = NULL; + if (event->packet) + m_packet = event->packet; + + if (m_packet) + enet_packet_destroy(m_packet); // we got all we need, just remove the data. + m_packet = NULL; + + std::vector peers = NetworkManager::getInstance()->getPeers(); + peer = new STKPeer*; + *peer = NULL; + for (unsigned int i = 0; i < peers.size(); i++) + { + if (peers[i]->m_peer == event->peer) + { + *peer = peers[i]; + Log::verbose("Event", "The peer you sought has been found on %lx", (long int)(peer)); + return; + } + } + if (*peer == NULL) // peer does not exist, create him + { + STKPeer* new_peer = new STKPeer(); + new_peer->m_peer = event->peer; + *peer = new_peer; + Log::debug("Event", "Creating a new peer, address are STKPeer:%lx, Peer:%lx", (long int)(new_peer), (long int)(event->peer)); + } +} + +Event::Event(const Event& event) +{ + m_packet = NULL; + m_data = event.m_data; + // copy the peer + peer = event.peer; + type = event.type; +} + +Event::~Event() +{ + peer = NULL; + m_packet = NULL; +} + +void Event::removeFront(int size) +{ + m_data.removeFront(size); +} + diff --git a/src/network/event.hpp b/src/network/event.hpp new file mode 100644 index 000000000..cf5bf6a6f --- /dev/null +++ b/src/network/event.hpp @@ -0,0 +1,87 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file event.hpp + * \brief Contains an interface to store network events, like connections, + * disconnections and messages. + */ + +#ifndef EVENT_HPP +#define EVENT_HPP + +#include "network/stk_peer.hpp" +#include "network/network_string.hpp" +#include "utils/types.hpp" + +/*! + * \enum EVENT_TYPE + * \brief Represents a network event type. + */ +enum EVENT_TYPE +{ + EVENT_TYPE_CONNECTED, //!< A peer is connected + EVENT_TYPE_DISCONNECTED,//!< A peer is disconnected + EVENT_TYPE_MESSAGE //!< A message between server and client protocols +}; + +/*! + * \class Event + * \brief Class representing an event that need to pass trough the system. + * This is used to remove ENet dependency in the network. + * It interfaces the ENetEvent structure. + * The user has to be extremely careful about the peer. + * Indeed, when packets are logged, the state of the peer cannot be stored at + * all times, and then the user of this class can rely only on the address/port + * of the peer, and not on values that might change over time. + */ +class Event +{ + public: + /*! \brief Constructor + * \param event : The event that needs to be translated. + */ + Event(ENetEvent* event); + /*! \brief Constructor + * \param event : The event to copy. + */ + Event(const Event& event); + /*! \brief Destructor + * frees the memory of the ENetPacket. + */ + ~Event(); + + /*! \brief Remove bytes at the beginning of data. + * \param size : The number of bytes to remove. + */ + void removeFront(int size); + + /*! \brief Get a copy of the data. + * \return A copy of the message data. This is empty for events like + * connection or disconnections. + */ + NetworkString data() const { return m_data; } + + EVENT_TYPE type; //!< Type of the event. + STKPeer** peer; //!< Pointer to the peer that triggered that event. + + private: + NetworkString m_data; //!< Copy of the data passed by the event. + ENetPacket* m_packet; //!< A pointer on the ENetPacket to be deleted. +}; + +#endif // EVENT_HPP diff --git a/src/network/flyable_info.hpp b/src/network/flyable_info.hpp deleted file mode 100644 index eedc33585..000000000 --- a/src/network/flyable_info.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 HEADER_FLYABLE_INFO_HPP -#define HEADER_FLYABLE_INFO_HPP - -#include "network/message.hpp" - -/** Class used to transfer information about projectiles from server to client. - * It contains only the coordinates, rotation, and explosion state. - */ -class FlyableInfo -{ -public: - Vec3 m_xyz; /** Position of object. */ - btQuaternion m_rotation; /** Orientation of object */ - bool m_exploded; /** If the object exploded in the current frame. */ - - /** Constructor to initialise all fields. - */ - FlyableInfo(const Vec3& xyz, const btQuaternion &rotation, bool exploded) : - m_xyz(xyz), m_rotation(rotation), m_exploded(exploded) - {}; - // ------------------------------------------------------------------------ - /** Allow this object to be stored in std::vector fields. - */ - FlyableInfo() {}; - // ------------------------------------------------------------------------ - /** Construct a FlyableInfo from a message (which is unpacked). - */ - FlyableInfo(Message *m) - { - m_xyz = m->getVec3(); - m_rotation = m->getQuaternion(); - m_exploded = m->getBool(); - } // FlyableInfo(Message) - // ------------------------------------------------------------------------ - /** Returns the length of the serialised message. */ - static int getLength() - { - return Message::getVec3Length() - + Message::getQuaternionLength() - + Message::getBoolLength(); - } // getLength - // ------------------------------------------------------------------------ - void serialise(Message *m) - { - m->addVec3(m_xyz); - m->addQuaternion(m_rotation); - m->addBool(m_exploded); - } // serialise -}; - -#endif - diff --git a/src/network/game_setup.cpp b/src/network/game_setup.cpp new file mode 100644 index 000000000..59d64ba9f --- /dev/null +++ b/src/network/game_setup.cpp @@ -0,0 +1,200 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/game_setup.hpp" + +#include "karts/abstract_kart.hpp" +#include "modes/world.hpp" +#include "race/race_manager.hpp" +#include "utils/log.hpp" + +//----------------------------------------------------------------------------- + +GameSetup::GameSetup() +{ + m_race_config = new RaceConfig(); +} + +//----------------------------------------------------------------------------- + +GameSetup::~GameSetup() +{ + // remove all players + for (unsigned int i = 0; i < m_players.size(); i++) + { + delete m_players[i]; + }; + m_players.clear(); +} + +//----------------------------------------------------------------------------- + +void GameSetup::addPlayer(NetworkPlayerProfile* profile) +{ + m_players.push_back(profile); + Log::info("GameSetup", "New player in the game setup. Global id : %u, " + "Race id : %d.", profile->user_profile->getID(), profile->race_id); +} + +//----------------------------------------------------------------------------- + +bool GameSetup::removePlayer(uint32_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->user_profile->getID() == id) + { + delete m_players[i]; + m_players.erase(m_players.begin()+i, m_players.begin()+i+1); + Log::verbose("GameSetup", "Removed a player from the game setup. " + "Remains %u.", m_players.size()); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- + +bool GameSetup::removePlayer(uint8_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->race_id == id) // check the given id + { + delete m_players[i]; + m_players.erase(m_players.begin()+i, m_players.begin()+i+1); + Log::verbose("GameSetup", "Removed a player from the game setup. " + "Remains %u.", m_players.size()); + return true; + } + if (m_players[i]->race_id > id) + { + m_players[i]->race_id--; // all indices in [0;n[ (n = #players) + } + } + return false; +} + +//----------------------------------------------------------------------------- + +void GameSetup::setPlayerKart(uint8_t id, std::string kart_name) +{ + bool found = false; + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->race_id == id) + { + m_players[i]->kart_name = kart_name; + Log::info("GameSetup::setPlayerKart", "Player %d took kart %s", + id, kart_name.c_str()); + found = true; + } + } + if (!found) + { + Log::info("GameSetup::setPlayerKart", "The player %d was unknown.", id); + } +} + +//----------------------------------------------------------------------------- + +void GameSetup::bindKartsToProfiles() +{ + World::KartList karts = World::getWorld()->getKarts(); + + for (unsigned int i = 0; i < m_players.size(); i++) + { + Log::info("GameSetup", "Player %d has id %d and kart %s", i, m_players[i]->race_id, m_players[i]->kart_name.c_str()); + } + for (unsigned int i = 0; i < karts.size(); i++) + { + Log::info("GameSetup", "Kart %d has id %d and kart %s", i, karts[i]->getWorldKartId(), karts[i]->getIdent().c_str()); + } + for (unsigned int j = 0; j < m_players.size(); j++) + { + bool found = false; + for (unsigned int i = 0 ; i < karts.size(); i++) + { + if (karts[i]->getIdent() == m_players[j]->kart_name) + { + m_players[j]->world_kart_id = karts[i]->getWorldKartId(); + found = true; + break; + } + } + if (!found) + { + Log::error("GameSetup", "Error while binding world kart ids to players profiles."); + } + } +} + +//----------------------------------------------------------------------------- + +const NetworkPlayerProfile* GameSetup::getProfile(uint32_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->user_profile->getID() == id) + { + return m_players[i]; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- + +const NetworkPlayerProfile* GameSetup::getProfile(uint8_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->race_id == id) + { + return m_players[i]; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- + +const NetworkPlayerProfile* GameSetup::getProfile(std::string kart_name) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->kart_name == kart_name) + { + return m_players[i]; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- + +bool GameSetup::isKartAvailable(std::string kart_name) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->kart_name == kart_name) + return false; + } + return true; +} diff --git a/src/network/game_setup.hpp b/src/network/game_setup.hpp new file mode 100644 index 000000000..b928e6ba3 --- /dev/null +++ b/src/network/game_setup.hpp @@ -0,0 +1,104 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file game_setup.hpp + */ + +#ifndef GAME_SETUP_HPP +#define GAME_SETUP_HPP + +#include "online/profile.hpp" +#include "network/race_config.hpp" + +#include +#include + +/*! \class PlayerProfile + * \brief Contains the profile of a player. + */ +class NetworkPlayerProfile +{ + public: + NetworkPlayerProfile() { race_id = 0; user_profile = NULL; } + ~NetworkPlayerProfile() {} + + uint8_t race_id; //!< The id of the player for the race + std::string kart_name; //!< The selected kart. + Online::Profile* user_profile; //!< Pointer to the lobby profile + uint8_t world_kart_id; //!< the kart id in the World class (pointer to AbstractKart) +}; + +/*! \class GameSetup + * \brief Used to store the needed data about the players that join a game. + * This class stores all the possible information about players in a lobby. + */ +class GameSetup +{ + public: + GameSetup(); + virtual ~GameSetup(); + + void addPlayer(NetworkPlayerProfile* profile); //!< Add a player. + bool removePlayer(uint32_t id); //!< Remove a player by id. + bool removePlayer(uint8_t id); //!< Remove a player by local id. + void setPlayerKart(uint8_t id, std::string kart_name); //!< Set the kart of a player + void bindKartsToProfiles(); //!< Sets the right world_kart_id in profiles + + /** \brief Get the players that are in the game + * \return A vector containing pointers on the players profiles. + */ + std::vector getPlayers() { return m_players; } + int getPlayerCount() { return m_players.size(); } + /*! \brief Get a network player profile matching a universal id. + * \param id : Global id of the player (the one in the SQL database) + * \return The profile of the player matching the id. + */ + const NetworkPlayerProfile* getProfile(uint32_t id); + /*! \brief Get a network player profile matching a kart name. + * \param kart_name : Name of the kart used by the player. + * \return The profile of the player having the kart kart_name + */ + const NetworkPlayerProfile* getProfile(uint8_t id); + /*! \brief Get a network player profile matching a kart name. + * \param kart_name : Name of the kart used by the player. + * \return The profile of the player having the kart kart_name. + */ + const NetworkPlayerProfile* getProfile(std::string kart_name); + + /*! \brief Used to know if a kart is available. + * \param kart_name : Name of the kart to check. + * \return True if the kart hasn't been selected yet, false elseway. + */ + bool isKartAvailable(std::string kart_name); + /*! \brief Used to know if a kart is playable. + * \param kart_name : Name of the kart to check. + * \return True if the kart is playable (standard kart). + * Currently this is always true as the kart selection screen shows + * only the standard karts. + */ + bool isKartAllowed(std::string kart_name) {return true; } + + RaceConfig* getRaceConfig() { return m_race_config; } + + protected: + std::vector m_players; //!< Information about players + NetworkPlayerProfile m_self_profile; //!< Information about self (client only) + RaceConfig* m_race_config; +}; + +#endif // GAME_SETUP_HPP diff --git a/src/network/item_info.hpp b/src/network/item_info.hpp deleted file mode 100644 index 0334e2ee0..000000000 --- a/src/network/item_info.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 HEADER_ITEM_INFO_HPP -#define HEADER_ITEM_INFO_HPP -/** Class used to transfer information about collected items from -* server to client. -*/ -class ItemInfo -{ -public: - /** Kart id (in world) of the kart collecting the item. */ - unsigned char m_kart_id; - /** Index of the collected item. This is set to -1 if a kart - * triggers a rescue (i.e. attaches the butterfly).] - */ - short m_item_id; - /** Additional info used, depending on item type. This is usually - * the type of the collected item. - */ - char m_add_info; - - /** Constructor to initialise all fields. */ - ItemInfo(int kart, int item, char add_info) : - m_kart_id(kart), m_item_id(item), - m_add_info(add_info) - {} - // ------------------------------------------------------------- - /** Construct ItemInfo from a message (which is unpacked). */ - ItemInfo(Message *m) - { - m_kart_id = m->getChar(); - m_item_id = m->getShort(); - m_add_info = m->getChar(); - } - // ------------------------------------------------------------- - /*** Returns size in bytes necessary to store ItemInfo. */ - static int getLength() {return 2*Message::getCharLength() - + Message::getShortLength();} - // ------------------------------------------------------------- - /** Serialises this object into the message object */ - void serialise(Message *m) - { - m->addChar(m_kart_id); - m->addShort(m_item_id); - m->addChar(m_add_info); - } // serialise -}; // ItemInfo - -#endif - diff --git a/src/network/kart_control_message.cpp b/src/network/kart_control_message.cpp deleted file mode 100644 index db6cea020..000000000 --- a/src/network/kart_control_message.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 "network/kart_control_message.hpp" - -#include "karts/controller/controller.hpp" -#include "modes/world.hpp" -#include "network/network_kart.hpp" - -KartControlMessage::KartControlMessage() - : Message(Message::MT_KART_CONTROL) -{ - World *world=World::getWorld(); - unsigned int num_local_players = race_manager->getNumLocalPlayers(); - unsigned int control_size = KartControl::getLength(); - allocate(control_size*num_local_players); - for(unsigned int i=0; igetLocalPlayerKart(i); - const KartControl& controls = kart->getControls(); - controls.serialise(this); - } -} // KartControlMessage -// ---------------------------------------------------------------------------- -/** Receives a kart control message. - * \param kart_id_offset is the global id of the first kart on the host from - * which this packet was received. - */ -KartControlMessage::KartControlMessage(ENetPacket* pkt, int kart_id_offset, - int num_local_players) - : Message(pkt, MT_KART_CONTROL) -{ - // FIXME: This probably does not work anymore - it assume that - // num_local_Players is the number of all local karts, while it might - // only be the number of all network karts. - for(int i=kart_id_offset; igetKart(i); - if(kart->getController()->isNetworkController()) - { - ((NetworkKart*)kart)->setControl(kc); - } - } -}; // KartControlMessage - diff --git a/src/network/kart_update_message.cpp b/src/network/kart_update_message.cpp deleted file mode 100644 index 2b16cc0e9..000000000 --- a/src/network/kart_update_message.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 "network/kart_update_message.hpp" - -#include "karts/abstract_kart.hpp" -#include "modes/world.hpp" - -KartUpdateMessage::KartUpdateMessage() - : Message(Message::MT_KART_INFO) -{ - World *world = World::getWorld(); - unsigned int num_karts = world->getNumKarts(); - - // Send the number of karts and for each kart the compressed - // control structure (3 ints) and xyz,hpr (4 floats: quaternion: - allocate(getCharLength()+ - num_karts*(KartControl::getLength() + getVec3Length() - +getQuaternionLength()) ); - addChar(num_karts); - for(unsigned int i=0; igetKart(i); - const KartControl& kc=kart->getControls(); - kc.serialise(this); - addVec3(kart->getXYZ()); - addQuaternion(kart->getRotation()); - } // for i -} // KartUpdateMessage -// ---------------------------------------------------------------------------- -KartUpdateMessage::KartUpdateMessage(ENetPacket* pkt) - : Message(pkt, MT_KART_INFO) -{ - World *world = World::getWorld(); - unsigned int num_karts = getInt(); - for(unsigned int i=0; igetKart(i); - kart->setXYZ(xyz); - kart->setRotation(q); - } // for i -}; // KartUpdateMessage - diff --git a/src/network/message.cpp b/src/network/message.cpp deleted file mode 100644 index b1f754c02..000000000 --- a/src/network/message.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak -// -// 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 "network/message.hpp" - -#include -#include -#include -#include - -/** Creates a message to be sent. - * This only initialised the data structures, it does not reserve any memory. - * A call to allocate() is therefore necessary. - * \param type The type of the message - */ -Message::Message(MessageType type) -{ - assert(sizeof(int)==4); - m_type = type; - m_pos = 0; - m_data_size = -1; - m_data = NULL; - m_needs_destroy = 0; // enet destroys message after send -} // Message - -// ---------------------------------------------------------------------------- -/** Handles a received message. - * The message in pkt is received, and the message type is checked. - * \param pkt The ENetPacket - * \param m The type of the message. The type is checked via an assert! - */ - -Message::Message(ENetPacket* pkt, MessageType m) -{ - receive(pkt, m); -} - -// ---------------------------------------------------------------------------- -/** Loads the message in pkt, and checks for the correct message type. - * Paramaters: - * \param pkt The ENetPacket - * \param m The type of the message. The type is checked via an assert! - */ -void Message::receive(ENetPacket* pkt, MessageType m) -{ - assert(sizeof(int)==4); - m_pkt = pkt; - m_data_size = pkt->dataLength; - m_data = (char*)pkt->data; - m_type = (MessageType)m_data[0]; - if(m_type!=m) - printf("type %d %d\n",m_type,m); - assert(m_type==m); - m_pos = 1; - m_needs_destroy = true; -} // Message - -// ---------------------------------------------------------------------------- -/** Frees the memory allocated for this message. */ -Message::~Message() -{ - clear(); -} // ~Message - -// ---------------------------------------------------------------------------- -/** Frees the memory for a received message. - * Calls enet_packet_destroy if necessary (i.e. if the message was received). - * The memory for a message created to be sent does not need to be freed, it - * is handled by enet. */ -void Message::clear() -{ - if(m_needs_destroy) - enet_packet_destroy(m_pkt); -} // clear - -// ---------------------------------------------------------------------------- -/** Reserves the memory for a message. - * \param size Number of bytes to reserve. - */ -void Message::allocate(int size) -{ - m_data_size = size+1; - m_pkt = enet_packet_create (NULL, m_data_size, ENET_PACKET_FLAG_RELIABLE); - m_data = (char*)m_pkt->data; - m_data[0] = m_type; - m_pos = 1; -} // allocate - -// ---------------------------------------------------------------------------- -/** Adds an integer value to the message. - * \param data The integer value to add. - */ -void Message::addInt(int data) -{ - assert((int)(m_pos + sizeof(int)) <= m_data_size); - int l=htonl(data); - memcpy(m_data+m_pos, &l, sizeof(int)); - m_pos+=sizeof(int); -} // addInt - -// ---------------------------------------------------------------------------- -/** Extracts an integer value from a message. - * \return The extracted integer. - */ -int Message::getInt() -{ - m_pos+=sizeof(int); - return ntohl(*(int*)(&m_data[m_pos-sizeof(int)])); -} // getInt - -// ---------------------------------------------------------------------------- -/** Adds a short value to the message. - * \param data The integer value to add. - */ -void Message::addShort(short data) -{ - assert((int)(m_pos + sizeof(short)) <= m_data_size); - int l=htons(data); - memcpy(m_data+m_pos, &l, sizeof(short)); - m_pos+=sizeof(short); -} // addShort - -// ---------------------------------------------------------------------------- -/** Extracts a short value from a message. - * \return The short value. - */ -short Message::getShort() -{ - m_pos+=sizeof(short); - return ntohs(*(short*)(&m_data[m_pos-sizeof(short)])); -} // getShort - -// ---------------------------------------------------------------------------- -/** Adds a floating point value to the message. - * \param data Floating point value to add. - */ -void Message::addFloat(const float data) -{ - // The simple approach of using addInt(*(int*)&data) - // does not work (at least with optimisation on certain g++ versions, - // see getFloat for more details) - int n; - memcpy(&n, &data, sizeof(float)); - addInt(n); -} // addFloat -// ---------------------------------------------------------------------------- -float Message::getFloat() -{ - int i = getInt(); - float f; - memcpy(&f, &i, sizeof(int)); - return f; - // The 'obvious' way: - // float *f = (float*) &i; - // return *f; - // does NOT work, see http://www.velocityreviews.com/forums/showthread.php?t=537336 - // for details -} // getFloat - -// ---------------------------------------------------------------------------- -void Message::addString(const std::string &data) -{ - int len = data.size()+1; // copy 0 end byte - assert((int)(m_pos+len) <=m_data_size); - memcpy (&(m_data[m_pos]), data.c_str(), len); - m_pos += len; -} // addString - -// ---------------------------------------------------------------------------- -std::string Message::getString() -{ - char *str = (char*) &(m_data[m_pos]); - int len = strlen(str)+1; - m_pos += len; - return std::string(str); -} // getString - -// ---------------------------------------------------------------------------- -/** Returns the number of bytes necessary to store a string vector. - * \param vs std::vector - */ -int Message::getStringVectorLength(const std::vector& vs) -{ - int len=getShortLength(); - for(unsigned int i=0; i to the message. - */ -void Message::addStringVector(const std::vector& vs) -{ - assert(vs.size()<32767); - addShort(vs.size()); - for(unsigned short i=0; i Message::getStringVector() -{ - std::vector vs; - vs.resize(getShort()); - for(unsigned int i=0; i -#include -#include -#include -#include "btBulletDynamicsCommon.h" - -using std::memcpy; - -#include "enet/enet.h" - -#include "utils/vec3.hpp" - -// sjl: when a message is received, need to work out what kind of message it -// is and therefore what to do with it - -/** Base class to serialises/deserialises messages. - * This is the base class for all messages being exchange between client - * and server. It handles the interface to enet, and adds a message type - * (which is checked via an assert to help finding bugs by receiving a - * message of an incorrect type). It also takes care of endianess (though - * floats are converted via a byte swap, too - so it must be guaranteed - * that the float representation between all machines is identical). - */ -class Message -{ -public: - /** Contains all tags used in identifying a message. */ - enum MessageType {MT_CONNECT=1, MT_CHARACTER_INFO, MT_CHARACTER_CONFIRM, - MT_RACE_INFO, MT_RACE_START, MT_WORLD_LOADED, - MT_KART_INFO, MT_KART_CONTROL, MT_RACE_STATE, - MT_RACE_RESULT, MT_RACE_RESULT_ACK - }; -private: - ENetPacket *m_pkt; - char *m_data; - MessageType m_type; - int m_data_size; - unsigned int m_pos; // simple stack counter for constructing packet data - bool m_needs_destroy; // only received messages need to be destroyed - -public: - void addInt(int data); - void addShort(short data); - void addString(const std::string &data); - void addStringVector(const std::vector& vs); - void addUInt(unsigned int data) { addInt(*(int*)&data); } - void addFloat(const float data); - void addBool(bool data) { addChar(data?1:0); } - void addChar(char data) { addCharArray((char*)&data,1);} - void addCharArray(char *c, unsigned int n=1) - { assert((int)(m_pos+n)<=m_data_size); - memcpy(m_data+m_pos,c,n); - m_pos+=n; } -#ifndef WIN32 // on windows size_t is unsigned int - void addSizeT(size_t data) { addInt((int)data); } -#endif - void addIntArray(int *d, unsigned int n) - { for(unsigned int i=0; - i - getStringVector(); - char getChar() {char c;getCharArray(&c,1); - return c; } - void getCharArray(char *c, int n=1) {memcpy(c,m_data+m_pos,n); - m_pos+=n; - return; } - Vec3 getVec3() { Vec3 v; - v.setX(getFloat()); - v.setY(getFloat()); - v.setZ(getFloat()); - return v; } - btQuaternion getQuaternion() { btQuaternion q; - q.setX(getFloat()); - q.setY(getFloat()); - q.setZ(getFloat()); - q.setW(getFloat()); - return q; } - static int getIntLength() { return sizeof(int); } - static int getUIntLength() { return sizeof(int); } - static int getShortLength() { return sizeof(short); } - static int getCharLength() { return sizeof(char); } - static int getBoolLength() { return sizeof(char); } - static int getFloatLength() { return sizeof(float); } - static int getStringLength(const std::string& s) { return s.size()+1;} - static int getVec3Length() { return 3*sizeof(float); } - static int getQuaternionLength() { return 4*sizeof(float); } - static int getStringVectorLength(const std::vector& vs); -#ifndef WIN32 - static int getSizeTLength(size_t n) { return sizeof(int); } -#endif - -public: - Message(MessageType m); - Message(ENetPacket *pkt, MessageType m); - void receive(ENetPacket *pkt, MessageType m); - ~Message(); - void clear(); - void allocate(int size); - MessageType getType() const { return m_type; } - ENetPacket* getPacket() const { assert(m_data_size>-1); return m_pkt; } - /** Return the type of a message without unserialising the message */ - static MessageType peekType(ENetPacket *pkt) - { return (MessageType)pkt->data[0];} - -}; // Message - - -#endif - diff --git a/src/network/kart_control_message.hpp b/src/network/network_interface.cpp similarity index 68% rename from src/network/kart_control_message.hpp rename to src/network/network_interface.cpp index 6127f62b9..262083330 100644 --- a/src/network/kart_control_message.hpp +++ b/src/network/network_interface.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,16 +16,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_KART_CONTROL_MESSAGE_HPP -#define HEADER_KART_CONTROL_MESSAGE_HPP +#include "network/network_interface.hpp" -#include "network/message.hpp" -class KartControlMessage : public Message +NetworkInterface::NetworkInterface() { -public: - KartControlMessage(); - KartControlMessage(ENetPacket* pkt, int kart_id_offset, - int num_local_players); -}; // KartUpdateMessage -#endif +} + +NetworkInterface::~NetworkInterface() +{ +} diff --git a/src/network/network_interface.hpp b/src/network/network_interface.hpp new file mode 100644 index 000000000..1861de115 --- /dev/null +++ b/src/network/network_interface.hpp @@ -0,0 +1,53 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file network_interface.hpp + * \brief Defines an interface to network middle-level functions. + */ + +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H + +#include "network/singleton.hpp" +#include "network/types.hpp" +#include "network/network_manager.hpp" + +#include +#include + +/** \class NetworkInterface + * \ingroup network + */ +class NetworkInterface : public Singleton +{ + friend class Singleton; + public: + + /*! \brief Used to init the network. + * \param server : True if we're a server. + */ + void initNetwork(bool server); + + protected: + // protected functions + NetworkInterface(); + virtual ~NetworkInterface(); + +}; + +#endif // NETWORK_INTERFACE_H diff --git a/src/network/network_kart.cpp b/src/network/network_kart.cpp deleted file mode 100644 index 42cf8a3cc..000000000 --- a/src/network/network_kart.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 SuperTuxKart-Team, Joerg Henrichs, Steve Baker -// -// 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 "network/network_manager.hpp" -#include "network/network_kart.hpp" - -/** A network kart. On the server, it receives its control information (steering etc) - from the network manager. - */ -NetworkKart::NetworkKart(const std::string &kart_name, - unsigned int world_kart_id, int position, - const btTransform &init_transform, - int global_player_id, - RaceManager::KartType type) - : Kart(kart_name, world_kart_id, position, - init_transform) -{ - m_global_player_id = global_player_id; -} // NetworkKart - -// ---------------------------------------------------------------------------- -void NetworkKart::setControl(const KartControl& kc) -{ - assert(network_manager->getMode()==NetworkManager::NW_SERVER); - m_controls = kc; -} // setControl - diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 2554e070d..a3d5d801c 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -18,723 +18,228 @@ #include "network/network_manager.hpp" -#include "config/stk_config.hpp" -#include "config/user_config.hpp" -#include "karts/kart_properties_manager.hpp" -#include "modes/world.hpp" -#include "network/connect_message.hpp" -#include "network/character_info_message.hpp" -#include "network/character_selected_message.hpp" -#include "network/race_info_message.hpp" -#include "network/race_start_message.hpp" -#include "network/world_loaded_message.hpp" -#include "network/race_state.hpp" -#include "network/kart_control_message.hpp" -#include "network/character_confirm_message.hpp" -#include "network/race_result_message.hpp" -#include "network/race_result_ack_message.hpp" -#include "race/race_manager.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_public_address.hpp" -NetworkManager* network_manager = 0; +#include "network/protocol_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/server_network_manager.hpp" + +#include "utils/log.hpp" + +#include +#include + +//----------------------------------------------------------------------------- NetworkManager::NetworkManager() { - m_mode = NW_NONE; - m_state = NS_ACCEPT_CONNECTIONS; - m_host = NULL; + m_public_address.ip = 0; + m_public_address.port = 0; + m_localhost = NULL; + m_game_setup = NULL; +} - m_num_clients = 0; - m_host_id = 0; +//----------------------------------------------------------------------------- - if (enet_initialize () != 0) - { - fprintf (stderr, "An error occurred while initializing ENet.\n"); - exit(-1); - } -} // NetworkManager - -// ----------------------------------------------------------------------------- -bool NetworkManager::initialiseConnections() -{ - switch(m_mode) - { - case NW_NONE: return true; - case NW_CLIENT: return initClient(); - case NW_SERVER: return initServer(); - } - return true; -} // NetworkManager - -// ----------------------------------------------------------------------------- NetworkManager::~NetworkManager() { - if(m_mode==NW_SERVER || m_mode==NW_CLIENT) enet_host_destroy(m_host); - enet_deinitialize(); -} // ~NetworkManager + ProtocolManager::kill(); -// ----------------------------------------------------------------------------- -bool NetworkManager::initServer() -{ - ENetAddress address; - address.host = ENET_HOST_ANY; - address.port = UserConfigParams::m_server_port; - - m_host = enet_host_create (& address /* the address to bind the server host to */, - stk_config->m_max_karts /* number of connections */, - 0 /* channel limit */, - 0 /* incoming bandwidth */, - 0 /* outgoing bandwidth */ ); - if (m_host == NULL) + if (m_localhost) + delete m_localhost; + while(!m_peers.empty()) { - fprintf (stderr, - "An error occurred while trying to create an ENet server host.\n" - "Progressing in non-network mode\n"); - m_mode = NW_NONE; - return false; + delete m_peers.back(); + m_peers.pop_back(); } +} - m_server = NULL; - m_clients.push_back(NULL); // server has host_id=0, so put a dummy entry at 0 in client array +//----------------------------------------------------------------------------- - m_client_names.push_back("server"); - return true; -} // initServer - -// ----------------------------------------------------------------------------- -/** Initialises the client. This function tries to connect to the server. - */ -bool NetworkManager::initClient() +void NetworkManager::run() { - m_host = enet_host_create (NULL /* create a client host */, - 1 /* only allow 1 outgoing connection */, - 0 /* channel limit */, - 0 /* downstream bandwidth unlimited */, - 0 /* upstream bandwidth unlimited */ ); + // create the protocol manager + ProtocolManager::getInstance(); +} - if (m_host == NULL) +void NetworkManager::reset() +{ + if (m_localhost) + delete m_localhost; + m_localhost = NULL; + while(!m_peers.empty()) { - fprintf (stderr, - "An error occurred while trying to create an ENet client host.\n"); - return false; + delete m_peers.back(); + m_peers.pop_back(); } +} - ENetAddress address; - ENetEvent event; - ENetPeer *peer; - - enet_address_set_host (& address, UserConfigParams::m_server_address.c_str()); - address.port = UserConfigParams::m_server_port; - - /* Initiate the connection, allocating the two channels 0 and 1. */ - peer = enet_host_connect (m_host, &address, 2, 0); - - if (peer == NULL) - { - fprintf(stderr, - "No available peers for initiating an ENet connection.\n"); - return false; - } - - /* Wait up to 5 seconds for the connection attempt to succeed. */ - if (enet_host_service (m_host, & event, 5000) <= 0 || - event.type != ENET_EVENT_TYPE_CONNECT) - { - /* Either the 5 seconds are up or a disconnect event was */ - /* received. Reset the peer in the event the 5 seconds */ - /* had run out without any significant event. */ - enet_peer_reset (peer); - - fprintf(stderr, "Connection to '%s:%d' failed.\n", - UserConfigParams::m_server_address.c_str(), (int)UserConfigParams::m_server_port); - return false; - } - m_server = peer; - return true; -} // initClient - -// ---------------------------------------------------------------------------- -/** Switches the network manager to client mode. This function sets the state - * to waiting_for_chars (so that the message from the server containing all - * available characters can be received). - */ -void NetworkManager::becomeClient() +void NetworkManager::abort() { - m_mode = NW_CLIENT; - m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS; -} // becomeClient + m_localhost->stopListening(); + reset(); + ProtocolManager::getInstance()->abort(); +} -// ---------------------------------------------------------------------------- -/** Switches the network manager to server mode. This function sets the state - * to accepting connections. - */ -void NetworkManager::becomeServer() +//----------------------------------------------------------------------------- + +bool NetworkManager::connect(TransportAddress peer) { - m_mode = NW_SERVER; - m_state = NS_ACCEPT_CONNECTIONS; -} // becomeServer + if (peerExists(peer)) + return isConnectedTo(peer); -// ---------------------------------------------------------------------------- -/** Called in case of an error, to switch back to non-networking mode. -*/ -void NetworkManager::disableNetworking() + return STKPeer::connectToHost(m_localhost, peer, 2, 0); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::setManualSocketsMode(bool manual) { - m_mode=NW_NONE; - if (m_host != NULL) - { - enet_host_destroy(m_host); - m_host = NULL; - } - // FIXME: what other enet data structures do we have to free/reset??? - -} // disableNetworking - -// ---------------------------------------------------------------------------- -void NetworkManager::handleNewConnection(ENetEvent *event) -{ - // Only accept while waiting for connections - if(m_state!=NS_ACCEPT_CONNECTIONS) return; - - // The logical connection (from STK point of view) happens when - // the connection message is received. But for now reserve the - // space in the data structures (e.g. in case that two connects - // happen before a connect message is received - m_client_names.push_back("NOT SET YET"); - m_clients.push_back(event->peer); - event->peer->data = (void*)int(m_clients.size()-1); // save hostid in peer data - -} // handleNewConnection - -// ---------------------------------------------------------------------------- -void NetworkManager::handleDisconnection(ENetEvent *event) -{ - if(m_state!=NS_ACCEPT_CONNECTIONS) - { - fprintf(stderr, "Disconnect while in race - close your eyes and hope for the best.\n"); - return; - } - fprintf(stderr, "%x:%d disconnected (host id %d).\n", event->peer->address.host, - event->peer->address.port, (int)(long)event->peer->data ); - m_num_clients--; -} // handleDisconnection - -// ---------------------------------------------------------------------------- -void NetworkManager::handleMessageAtServer(ENetEvent *event) -{ // handle message at server (from client) - - switch(m_state) - { - case NS_ACCEPT_CONNECTIONS: - { - ConnectMessage m(event->packet); - m_client_names[(int)(long)event->peer->data] = m.getId(); - m_num_clients++; - return; - } - case NS_KART_CONFIRMED: // Fall through - case NS_CHARACTER_SELECT: - { - CharacterSelectedMessage m(event->packet); - unsigned int hostid=(unsigned int)(long)event->peer->data; - assert(hostid>=1 && hostid<=m_num_clients); - if(m_num_local_players[hostid]==-1) // first package from that host - { - m_num_local_players[hostid] = m.getNumPlayers(); - m_num_all_players += m.getNumPlayers(); - // count how many hosts have sent (at least) one message - m_barrier_count ++; - } - RemoteKartInfo ki=m.getKartInfo(); - ki.setHostId(hostid); - m_kart_info.push_back(ki); - - int kart_id = kart_properties_manager->getKartId(ki.getKartName()); - kart_properties_manager->testAndSetKart(kart_id); - // TODO - character selection screen in networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters(); - */ - - // Broadcast the information about a selected kart to all clients - CharacterConfirmMessage ccm(ki.getKartName(), hostid); - broadcastToClients(ccm); - // See if this was the last message, i.e. we have received at least - // one message from each client, and the size of the kart_info - // array is the same as the number of all players (which does not - // yet include the number of players on the host). - if(m_barrier_count == (int)m_num_clients && - m_num_all_players==(int)m_kart_info.size()) - { - // we can't send the race info yet, since the server might - // not yet have selected all characters! - m_state = NS_ALL_REMOTE_CHARACTERS_DONE; - } - break; - } - case NS_READY_SET_GO_BARRIER: - { - m_barrier_count ++; - if(m_barrier_count==(int)m_num_clients) - { - m_state = NS_RACING; - RaceStartMessage m; - broadcastToClients(m); - } - break; - } - case NS_RACE_RESULT_BARRIER: - { - // Other message, esp. kart control, are silently ignored. - // FIXME: we might want to make sure that no such message actually arrives - if(Message::peekType(event->packet)!=Message::MT_RACE_RESULT_ACK) - { - enet_packet_destroy(event->packet); - return; - } - m_barrier_count++; - if(m_barrier_count==(int)m_num_clients) - { - m_state = NS_MAIN_MENU; - } - break; - } - default: assert(0); // should not happen - } // switch m_state -} // handleMessageAtServer - -// ---------------------------------------------------------------------------- -void NetworkManager::handleMessageAtClient(ENetEvent *event) -{ // handle message at client (from server) - switch(m_state) - { - case NS_WAIT_FOR_AVAILABLE_CHARACTERS: - { - CharacterInfoMessage m(event->packet); - // FIXME: handle list of available characters - m_state = NS_CHARACTER_SELECT; - break; - } - case NS_CHARACTER_SELECT: - { - CharacterConfirmMessage m(event->packet); - kart_properties_manager->selectKartName(m.getKartName()); - // TODO - karts selection screen in networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters(); - */ - break; - } - case NS_WAIT_FOR_KART_CONFIRMATION: - { - CharacterConfirmMessage m(event->packet); - kart_properties_manager->selectKartName(m.getKartName()); - - // If the current menu is the character selection menu, - // update the menu so that the newly taken character is removed. - // TODO - kart selection screen and networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters();*/ - // Check if we received a message about the kart we just selected. - // If so, the menu needs to progress, otherwise a different kart - // must be selected by the current player. - if(m.getKartName()==m_kart_to_confirm) - { - int host_id = m.getHostId(); - m_state = (host_id == getMyHostId()) ? NS_KART_CONFIRMED - : NS_CHARACTER_SELECT; - } // m.getkartName()==m_kart_to_confirm - break; - } // wait for kart confirmation - case NS_WAIT_FOR_RACE_DATA: - { - // It is possible that character confirm messages arrive at the - // client when it has already left the character selection screen. - // In this case the messages can simply be ignored. - if(Message::peekType(event->packet)==Message::MT_CHARACTER_CONFIRM) - { - // Receiving it will automatically free the memory. - CharacterConfirmMessage m(event->packet); - return; - } - RaceInfoMessage m(event->packet); - // The constructor actually sets the information in the race manager - m_state = NS_LOADING_WORLD; - break; - } - case NS_READY_SET_GO_BARRIER: - { - // Not actually needed, but the destructor of RaceStartMessage - // will free the memory of the event. - RaceStartMessage m(event->packet); - m_state = NS_RACING; - break; - } - case NS_RACING: - { - assert(false); // should never be here while racing - break; - } - case NS_RACE_RESULT_BARRIER: - { - RaceResultAckMessage message(event->packet); - // TODO - race results menu in networking - /* - RaceResultsGUI *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->setSelectedWidget(message.getSelectedMenu());*/ - m_state = NS_RACE_RESULT_BARRIER_OVER; - break; - } - default: - { - printf("received unknown message: type %d\n", - Message::peekType(event->packet)); - // assert(0); // should not happen - } - } // switch m_state -} // handleMessageAtClient - -// ---------------------------------------------------------------------------- -void NetworkManager::update(float dt) -{ - if(m_mode==NW_NONE) return; - // Messages during racing are handled in the sendUpdates/receiveUpdate - // calls, so don't do anything in this case. - if(m_state==NS_RACING) return; - - ENetEvent event; - int result = enet_host_service (m_host, &event, 0); - if(result==0) return; - if(result<0) - { - fprintf(stderr, "Error while receiving messages -> ignored.\n"); - return; - } - switch (event.type) - { - case ENET_EVENT_TYPE_CONNECT: handleNewConnection(&event); break; - case ENET_EVENT_TYPE_RECEIVE: - if(m_mode==NW_SERVER) - handleMessageAtServer(&event); - else - handleMessageAtClient(&event); - break; - case ENET_EVENT_TYPE_DISCONNECT: handleDisconnection(&event); break; - case ENET_EVENT_TYPE_NONE: break; - } -} // update - -// ---------------------------------------------------------------------------- -void NetworkManager::broadcastToClients(Message &m) -{ - enet_host_broadcast(m_host, 0, m.getPacket()); - enet_host_flush(m_host); -} // broadcastToClients - -// ---------------------------------------------------------------------------- -void NetworkManager::sendToServer(Message &m) -{ - enet_peer_send(m_server, 0, m.getPacket()); - enet_host_flush(m_host); -} // sendToServer - -// ---------------------------------------------------------------------------- -/** Cleans up character related data structures. Must be called before any - * character related data is set. - */ -void NetworkManager::initCharacterDataStructures() -{ - // This is called the first time the character selection menu is displayed - - if(m_mode==NW_CLIENT) - { - // Change state to wait for list of characters from server - m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS; - } - else // Server or no network - { - if(m_mode==NW_SERVER) - { - // server: create message with all valid characters - // ================================================ - for(unsigned int i=1; i<=m_num_clients; i++) - { - CharacterInfoMessage m(i); - enet_peer_send(m_clients[i], 0, m.getPacket()); - } - enet_host_flush(m_host); - } - // For server and no network: - // ========================== - // Prepare the data structures to receive and - // store information from all clients. - m_num_local_players.clear(); - // Server (hostid 0) is not included in the num_clients count. So to - // be able to use the hostid as index, we have to allocate one - // additional element. - m_num_local_players.resize(m_num_clients+1, -1); - m_num_local_players[0] = race_manager->getNumLocalPlayers(); - m_kart_info.clear(); - m_num_all_players = 0; - // use barrier count to see if we had at least one message from each host - m_barrier_count = 0; - m_state = NS_CHARACTER_SELECT; - } - -} // switchTocharacterSelection - -// ---------------------------------------------------------------------------- -/** Called on the client to send the data about the selected kart to the - * server and wait for confirmation. - * \param player_id Local id of the player which selected the kart. - * \param kart_id Identifier of the selected kart. this is used to wait till - * a message about this kart is received back from the server. - */ -void NetworkManager::sendCharacterSelected(int player_id, - const std::string &kart_id) -{ - if(m_mode==NW_SERVER) - { - CharacterConfirmMessage ccm(kart_id, getMyHostId()); - broadcastToClients(ccm); - } - else if(m_mode==NW_CLIENT) - { - CharacterSelectedMessage m(player_id); - sendToServer(m); - // Wait till we receive confirmation about the selected character. - m_state = NS_WAIT_FOR_KART_CONFIRMATION; - m_kart_to_confirm = kart_id; - } -} // sendCharacterSelected - -// ---------------------------------------------------------------------------- -void NetworkManager::waitForRaceInformation() -{ - m_state = NS_WAIT_FOR_RACE_DATA; -} // waitForRaceInformation - -// ---------------------------------------------------------------------------- -void NetworkManager::worldLoaded() -{ - if(m_mode==NW_CLIENT) - { - WorldLoadedMessage m; - sendToServer(m); - m_state = NS_READY_SET_GO_BARRIER; - } -} // worldLoaded - -// ---------------------------------------------------------------------------- -/** Receive and store the information from sendKartsInformation() -*/ -void NetworkManager::setupPlayerKartInfo() -{ - // Not sure if this should be here, but without it extra uncontrolled - // human players accumulate after each race. - m_kart_info.clear(); - - // Get the local kart info - for(unsigned int i=0; igetNumLocalPlayers(); i++) - m_kart_info.push_back(race_manager->getLocalKartInfo(i)); - - // Now sort by (hostid, playerid) - std::sort(m_kart_info.begin(), m_kart_info.end()); - - // Set the player kart information - race_manager->setNumPlayers(m_kart_info.size()); - - // Set the global player ID for each player - for(unsigned int i=0; isetPlayerKart(i, m_kart_info[i]); - } - - // Compute the id of the first kart from each host - m_kart_id_offset.resize(m_num_clients+1); - m_kart_id_offset[0]=0; - for(unsigned int i=1; i<=m_num_clients; i++) - m_kart_id_offset[i]=m_kart_id_offset[i-1]+m_num_local_players[i-1]; - - race_manager->computeRandomKartList(); -} // setupPlayerKartInfo - -// ---------------------------------------------------------------------------- -/** Sends the information from the race_manager to all clients. -*/ -void NetworkManager::sendRaceInformationToClients() -{ - if(m_mode==NW_SERVER) - { - setupPlayerKartInfo(); - RaceInfoMessage m(m_kart_info); - broadcastToClients(m); - } - beginReadySetGoBarrier(); -} // sendRaceInformationToClients - -// ---------------------------------------------------------------------------- -void NetworkManager::beginReadySetGoBarrier() -{ - m_state = NS_READY_SET_GO_BARRIER; - m_barrier_count = 0; - if(m_num_clients==0) m_state = NS_RACING; -} // beginReadySetGoBarrier -// ---------------------------------------------------------------------------- -void NetworkManager::sendConnectMessage() -{ - ConnectMessage msg; - sendToServer(msg); -} // sendConnectMessage -// ---------------------------------------------------------------------------- -/*** Send all kart controls and kart positions to all clients -*/ -void NetworkManager::sendUpdates() -{ - if(m_mode==NW_SERVER) - { - race_state->serialise(); - broadcastToClients(*race_state); - } - else if(m_mode==NW_CLIENT) - { - KartControlMessage m; - sendToServer(m); - } -} // sendUpdates - -// ---------------------------------------------------------------------------- -void NetworkManager::receiveUpdates() -{ - if(m_mode==NW_NONE) return; // do nothing if not networking - // The server receives m_num_clients messages, each client one message - int num_messages = m_mode==NW_SERVER ? m_num_clients : 1; - ENetEvent event; - bool correct=true; - - for(int i=0; ienterRaceOverState(); - return; - } - race_state->receive(event.packet); - } - } // for istopListening(); else + m_localhost->startListening(); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::notifyEvent(Event* event) +{ + Log::verbose("NetworkManager", "EVENT received of type %d", (int)(event->type)); + STKPeer* peer = *event->peer; + if (event->type == EVENT_TYPE_CONNECTED) { - broadcastToClients(m); + Log::info("NetworkManager", "A client has just connected. There are now %lu peers.", m_peers.size() + 1); + Log::debug("NetworkManager", "Addresses are : %lx, %lx, %lx", event->peer, *event->peer, peer); + // create the new peer: + m_peers.push_back(peer); } -} // sendRaceResultAck + if (event->type == EVENT_TYPE_MESSAGE) + { + uint32_t addr = peer->getAddress(); + Log::verbose("NetworkManager", "Message, Sender : %i.%i.%i.%i, message = \"%s\"", + ((addr>>24)&0xff), + ((addr>>16)&0xff), + ((addr>>8)&0xff), + (addr&0xff), event->data().c_str()); + + } + + // notify for the event now. + ProtocolManager::getInstance()->notifyEvent(event); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::sendPacket(STKPeer* peer, const NetworkString& data, bool reliable) +{ + if (peer) + peer->sendPacket(data, reliable); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::sendPacketExcept(STKPeer* peer, const NetworkString& data, bool reliable) +{ + for (unsigned int i = 0; i < m_peers.size(); i++) + { + STKPeer* p = m_peers[i]; + if (!p->isSamePeer(peer)) + { + p->sendPacket(data, reliable); + } + } +} + +//----------------------------------------------------------------------------- + +GameSetup* NetworkManager::setupNewGame() +{ + if (m_game_setup) + delete m_game_setup; + m_game_setup = NULL; + m_game_setup = new GameSetup(); + return m_game_setup; +} + +//----------------------------------------------------------------------------- + +void NetworkManager::disconnected() +{ + // delete the game setup + if (m_game_setup) + delete m_game_setup; + m_game_setup = NULL; + + // remove all peers + for (unsigned int i = 0; i < m_peers.size(); i++) + { + delete m_peers[i]; + m_peers[i] = NULL; + } + m_peers.clear(); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::setLogin(std::string username, std::string password) +{ + m_player_login.username = username; + m_player_login.password = password; +} + +//----------------------------------------------------------------------------- + +void NetworkManager::setPublicAddress(TransportAddress addr) +{ + m_public_address = addr; +} +//----------------------------------------------------------------------------- + +void NetworkManager::removePeer(STKPeer* peer) +{ + if (!peer || !peer->exists()) // peer does not exist (already removed) + return; + Log::debug("NetworkManager", "Disconnected host: %i.%i.%i.%i:%i", + peer->getAddress()>>24&0xff, + peer->getAddress()>>16&0xff, + peer->getAddress()>>8&0xff, + peer->getAddress()&0xff, + peer->getPort()); + // remove the peer: + bool removed = false; + for (unsigned int i = 0; i < m_peers.size(); i++) + { + if (m_peers[i]->isSamePeer(peer) && !removed) // remove only one + { + delete m_peers[i]; + m_peers.erase(m_peers.begin()+i, m_peers.begin()+i+1); + Log::verbose("NetworkManager", "The peer has been removed from the Network Manager."); + removed = true; + } + else if (m_peers[i]->isSamePeer(peer)) + { + Log::fatal("NetworkManager", "Multiple peers match the disconnected one."); + } + } + if (!removed) + Log::warn("NetworkManager", "The peer that has been disconnected was not registered by the Network Manager."); + + Log::info("NetworkManager", "Somebody is now disconnected. There are now %lu peers.", m_peers.size()); +} + +//----------------------------------------------------------------------------- + +bool NetworkManager::peerExists(TransportAddress peer) +{ + return m_localhost->peerExists(peer); +} + +//----------------------------------------------------------------------------- + +bool NetworkManager::isConnectedTo(TransportAddress peer) +{ + return m_localhost->isConnectedTo(peer); +} diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 83d55f556..40ec66971 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,107 +16,108 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NETWORK_MANAGER_HPP -#define HEADER_NETWORK_MANAGER_HPP +/*! \file network_manager.hpp + * \brief Instantiates the generic functionnalities of a network manager. + */ + +#ifndef NETWORKMANAGER_HPP +#define NETWORKMANAGER_HPP + +#include "network/stk_peer.hpp" +#include "network/stk_host.hpp" + +#include "network/protocol_manager.hpp" +#include "network/singleton.hpp" +#include "network/types.hpp" +#include "network/event.hpp" +#include "network/game_setup.hpp" -#include #include -#include "enet/enet.h" - -#include "network/remote_kart_info.hpp" - - -class Message; - -class NetworkManager +/** \class NetworkManager + * \brief Gives the general functions to use network communication. + * This class is in charge of storing the peers connected to this host. + * It also stores the host, and brings the functions to send messages to peers. + * It automatically dispatches the events or packets it receives. This class + * also stores the public address when known and the player login. + * Here are defined some functions that will be specifically implemented by + * the ServerNetworkManager and the ClientNetworkManager. + */ +class NetworkManager : public Singleton { -public: - // The mode the network manager is operating in - enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE}; + friend class Singleton; + public: + /** \brief Function to start the Network Manager (start threads) */ + virtual void run(); + /** \brief Function to reset the Network Manager. + * This function resets the peers and the listening host. + */ + virtual void reset(); + /** \brief Function that aborts the NetworkManager. + * This function will stop the listening, delete the host and stop + * threads that are related to networking. + */ + virtual void abort(); - // States for the finite state machine. First for server: - enum NetworkState {NS_MAIN_MENU, // before char sel gui - NS_ACCEPT_CONNECTIONS, // server: accept connections - NS_WAIT_FOR_AVAILABLE_CHARACTERS, // client: wait for list - NS_ALL_REMOTE_CHARACTERS_DONE, // server: all client data received - NS_WAIT_FOR_KART_CONFIRMATION, // client: wait for confirmation - // if character selection was ok - NS_KART_CONFIRMED, // Character was confirmed - NS_WAIT_FOR_RACE_DATA, // client: wait for race info - NS_READY_SET_GO_BARRIER, // c&s: barrier before r.s.g. - NS_CHARACTER_SELECT, // c&s: character select in progress - NS_LOADING_WORLD, // client: loading world - NS_RACING, - NS_WAIT_FOR_RACE_RESULT, // clients: waiting for race results - NS_RACE_RESULT_BARRIER , // Wait till all ack results - NS_RACE_RESULT_BARRIER_OVER // Barrier is over, goto next state - }; -private: + // network management functions + /** \brief Try to establish a connection to a given transport address. + * \param peer : The transport address which you want to connect to. + * \return True if we're successfully connected. False elseway. + */ + virtual bool connect(TransportAddress peer); + /** \brief Changes the socket working mode. + * Sockets can be in two modes : The ENet mode and a mode we will call + * the 'Raw' mode. In the ENet mode, the socket will be read as + * \param peer : The transport address which you want to connect to. + * \return True if we're successfully connected. False elseway. + */ + virtual void setManualSocketsMode(bool manual); - NetworkMode m_mode; - NetworkState m_state; - unsigned int m_num_clients; - std::vector m_kart_info; - int m_host_id; - std::vector m_client_names; - std::vector m_num_local_players; - std::vector m_kart_id_offset; // kart id of first kart on host i - int m_num_all_players; - int m_barrier_count; + // message/packets related functions + virtual void notifyEvent(Event* event); + virtual void sendPacket(const NetworkString& data, + bool reliable = true) = 0; + virtual void sendPacket(STKPeer* peer, + const NetworkString& data, + bool reliable = true); + virtual void sendPacketExcept(STKPeer* peer, + const NetworkString& data, + bool reliable = true); - ENetHost *m_host; // me - ENetPeer *m_server; // (clients only) - std::vector m_clients; // (server only) pos in vector is client host_id - /** Name of the kart that a client is waiting for confirmation for. */ - std::string m_kart_to_confirm; + // Game related functions + virtual GameSetup* setupNewGame(); //!< Creates a new game setup and returns it + virtual void disconnected(); //!< Called when you leave a server - bool initServer(); - bool initClient(); - void handleNewConnection(ENetEvent *event); - void handleMessageAtServer(ENetEvent *event); - void handleMessageAtClient(ENetEvent *event); - void handleDisconnection(ENetEvent *event); - // the first cast to long avoid compiler errors on 64 bit systems - // about lost precision, then cast long to int to get the right type - unsigned int getHostId(ENetPeer *p) const {return (int)(long)p->data; } + // raw data management + void setLogin(std::string username, std::string password); + void setPublicAddress(TransportAddress addr); + void removePeer(STKPeer* peer); - void sendToServer(Message &m); - void broadcastToClients(Message &m); -public: - NetworkManager(); - ~NetworkManager(); - void setMode(NetworkMode m) {m_mode = m; } - NetworkMode getMode() const {return m_mode; } - void becomeServer(); - void becomeClient(); - void setState(NetworkState s) {m_state = s; } - NetworkState getState() const {return m_state; } - int getMyHostId() const {return m_host_id; } - void setHostId(int host_id) {m_host_id = host_id; } - unsigned int getNumClients() const {return m_num_clients; } - const std::string& - getClientName(int i) const {return m_client_names[i];} - bool initialiseConnections(); - void update(float dt); + // getters + virtual bool peerExists(TransportAddress peer); + virtual bool isConnectedTo(TransportAddress peer); - void disableNetworking(); - void sendConnectMessage(); // client send initial info to server - void initCharacterDataStructures(); - void sendCharacterSelected(int player_id, const std::string &kartid); - void waitForRaceInformation(); - void worldLoaded(); - void setupPlayerKartInfo(); - void beginReadySetGoBarrier(); - void sendRaceInformationToClients(); - void sendUpdates(); - void receiveUpdates(); - void waitForClientData(); - void sendRaceResults(); - void beginRaceResultBarrier(); - void sendRaceResultAck(char menu_selection=-1); + virtual bool isServer() = 0; + inline bool isClient() { return !isServer(); } + bool isPlayingOnline() { return m_playing_online; } + STKHost* getHost() { return m_localhost; } + std::vector getPeers() { return m_peers; } + unsigned int getPeerCount() { return m_peers.size(); } + TransportAddress getPublicAddress() { return m_public_address; } + GameSetup* getGameSetup() { return m_game_setup; } + + protected: + NetworkManager(); + virtual ~NetworkManager(); + + // protected members + std::vector m_peers; + STKHost* m_localhost; + bool m_playing_online; + GameSetup* m_game_setup; + + TransportAddress m_public_address; + PlayerLogin m_player_login; }; -extern NetworkManager *network_manager; - -#endif +#endif // NETWORKMANAGER_HPP diff --git a/src/network/network_string.cpp b/src/network/network_string.cpp new file mode 100644 index 000000000..251c98fb5 --- /dev/null +++ b/src/network/network_string.cpp @@ -0,0 +1,8 @@ +#include "network/network_string.hpp" + +NetworkString operator+(NetworkString const& a, NetworkString const& b) +{ + NetworkString ns(a); + ns += b; + return ns; +} diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp new file mode 100644 index 000000000..fe6e018e8 --- /dev/null +++ b/src/network/network_string.hpp @@ -0,0 +1,256 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file network_string.hpp + * \brief Defines functions to easily manipulate 8-bit network destinated strings. + */ + +#ifndef NETWORK_STRING_HPP +#define NETWORK_STRING_HPP + +#include "utils/types.hpp" + +#include +#include +#include +#include + +typedef unsigned char uchar; + +/** \class NetworkString + * \brief Describes a chain of 8-bit unsigned integers. + * This class allows you to easily create and parse 8-bit strings. + */ +class NetworkString +{ + union { + float f; + uint8_t i[4]; + } f_as_i; // float as integer + union { + double d; + uint8_t i[8]; + } d_as_i; // double as integer + public: + NetworkString() { } + NetworkString(const uint8_t& value) { m_string.push_back(value); } + NetworkString(NetworkString const& copy) { m_string = copy.m_string; } + NetworkString(const std::string & value) { m_string = std::vector(value.begin(), value.end()); } + + NetworkString& removeFront(int size) + { + m_string.erase(m_string.begin(), m_string.begin()+size); + return *this; + } + NetworkString& remove(int pos, int size) + { + m_string.erase(m_string.begin()+pos, m_string.begin()+pos+size); + return *this; + } + + uint8_t operator[](const int& pos) const + { + return getUInt8(pos); + } + + NetworkString& addUInt8(const uint8_t& value) + { + m_string.push_back(value); + return *this; + } + inline NetworkString& ai8(const uint8_t& value) { return addUInt8(value); } + NetworkString& addUInt16(const uint16_t& value) + { + m_string.push_back((value>>8)&0xff); + m_string.push_back(value&0xff); + return *this; + } + inline NetworkString& ai16(const uint16_t& value) { return addUInt16(value); } + NetworkString& addUInt32(const uint32_t& value) + { + m_string.push_back((value>>24)&0xff); + m_string.push_back((value>>16)&0xff); + m_string.push_back((value>>8)&0xff); + m_string.push_back(value&0xff); + return *this; + } + inline NetworkString& ai32(const uint32_t& value) { return addUInt32(value); } + NetworkString& addInt(const int& value) + { + m_string.push_back((value>>24)&0xff); + m_string.push_back((value>>16)&0xff); + m_string.push_back((value>>8)&0xff); + m_string.push_back(value&0xff); + return *this; + } + inline NetworkString& ai(const int& value) { return addInt(value); } + NetworkString& addFloat(const float& value) //!< BEWARE OF PRECISION + { + assert(sizeof(float)==4); + f_as_i.f = value; + m_string.push_back(f_as_i.i[0]); + m_string.push_back(f_as_i.i[1]); + m_string.push_back(f_as_i.i[2]); + m_string.push_back(f_as_i.i[3]); + return *this; + } + inline NetworkString& af(const float& value) { return addFloat(value); } + NetworkString& addDouble(const double& value) //!< BEWARE OF PRECISION + { + assert(sizeof(double)==8); + d_as_i.d = value; + m_string.push_back(d_as_i.i[0]); + m_string.push_back(d_as_i.i[1]); + m_string.push_back(d_as_i.i[2]); + m_string.push_back(d_as_i.i[3]); + m_string.push_back(d_as_i.i[4]); + m_string.push_back(d_as_i.i[5]); + m_string.push_back(d_as_i.i[6]); + m_string.push_back(d_as_i.i[7]); + return *this; + } + inline NetworkString& ad(const double& value) { return addDouble(value); } + NetworkString& addChar(const char& value) + { + m_string.push_back((uint8_t)(value)); + return *this; + } + inline NetworkString& ac(const char& value) { return addChar(value); } + + NetworkString& addString(const std::string& value) + { + for (unsigned int i = 0; i < value.size(); i++) + m_string.push_back((uint8_t)(value[i])); + return *this; + } + inline NetworkString& as(const std::string& value) { return addString(value); } + + NetworkString& operator+=(NetworkString const& value) + { + m_string.insert( m_string.end(), value.m_string.begin(), value.m_string.end() ); + return *this; + } + + const char* c_str() const + { + std::string str(m_string.begin(), m_string.end()); + return str.c_str(); + } + int size() const + { + return m_string.size(); + } + + template + T get(int pos) const + { + int a = n; + T result = 0; + while(a--) + { + result <<= 8; // offset one byte + result += ((uint8_t)(m_string[pos+n-1-a]) & 0xff); // add the data to result + } + return result; + } + + inline int getInt(int pos = 0) const { return get(pos); } + inline uint32_t getUInt(int pos = 0) const { return get(pos); } + inline uint32_t getUInt32(int pos = 0) const { return get(pos); } + inline uint16_t getUInt16(int pos = 0) const { return get(pos); } + inline uint8_t getUInt8(int pos = 0) const { return get(pos); } + inline char getChar(int pos = 0) const { return get(pos); } + inline unsigned char getUChar(int pos = 0) const { return get(pos); } + std::string getString(int pos, int len) const { return std::string(m_string.begin()+pos, m_string.begin()+pos+len); } + + inline int gi(int pos = 0) const { return get(pos); } + inline uint32_t gui(int pos = 0) const { return get(pos); } + inline uint32_t gui32(int pos = 0) const { return get(pos); } + inline uint16_t gui16(int pos = 0) const { return get(pos); } + inline uint8_t gui8(int pos = 0) const { return get(pos); } + inline char gc(int pos = 0) const { return get(pos); } + inline unsigned char guc(int pos = 0) const { return get(pos); } + std::string gs(int pos, int len) const { return std::string(m_string.begin()+pos, m_string.begin()+pos+len); } + + double getDouble(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 8; i++) + d_as_i.i[i] = m_string[pos+i]; + return d_as_i.d; + } + float getFloat(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 4; i++) + f_as_i.i[i] = m_string[pos+i]; + return f_as_i.f; + } + + //! Functions to get while removing + template + T getAndRemove(int pos) + { + int a = n; + T result = 0; + while(a--) + { + result <<= 8; // offset one byte + result += ((uint8_t)(m_string[pos+n-1-a]) & 0xff); // add the data + } + remove(pos,n); + return result; + } + + inline int getAndRemoveInt(int pos = 0) { return getAndRemove(pos); } + inline uint32_t getAndRemoveUInt(int pos = 0) { return getAndRemove(pos); } + inline uint32_t getAndRemoveUInt32(int pos = 0) { return getAndRemove(pos); } + inline uint16_t getAndRemoveUInt16(int pos = 0) { return getAndRemove(pos); } + inline uint8_t getAndRemoveUInt8(int pos = 0) { return getAndRemove(pos); } + inline char getAndRemoveChar(int pos = 0) { return getAndRemove(pos); } + inline unsigned char getAndRemoveUChar(int pos = 0) { return getAndRemove(pos); } + double getAndRemoveDouble(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 8; i++) + d_as_i.i[i] = m_string[pos+i]; + return d_as_i.d; + remove(pos, 8); + } + float getAndRemoveFloat(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 4; i++) + f_as_i.i[i] = m_string[pos+i]; + return f_as_i.f; + remove(pos, 4); + } + + inline NetworkString& gui8(uint8_t* dst) { *dst = getAndRemoveUInt8(0); return *this; } + inline NetworkString& gui16(uint16_t* dst) { *dst = getAndRemoveUInt16(0); return *this; } + inline NetworkString& gui32(uint32_t* dst) { *dst = getAndRemoveUInt32(0); return *this; } + inline NetworkString& gui(uint32_t* dst) { *dst = getAndRemoveUInt32(0); return *this; } + inline NetworkString& gi(int* dst) { *dst = getAndRemoveInt(0); return *this; } + inline NetworkString& gc(char* dst) { *dst = getAndRemoveChar(0); return *this; } + inline NetworkString& guc(uchar* dst) { *dst = getAndRemoveUChar(0); return *this; } + inline NetworkString& gd(double* dst) { *dst = getAndRemoveDouble(0); return *this; } + inline NetworkString& gf(float* dst) { *dst = getAndRemoveFloat(0); return *this; } + + protected: + std::vector m_string; +}; + +NetworkString operator+(NetworkString const& a, NetworkString const& b); + +#endif // NETWORK_STRING_HPP diff --git a/src/network/network_world.cpp b/src/network/network_world.cpp new file mode 100644 index 000000000..6337911f4 --- /dev/null +++ b/src/network/network_world.cpp @@ -0,0 +1,78 @@ +#include "network/network_world.hpp" + +#include "network/network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/synchronization_protocol.hpp" +#include "network/protocols/controller_events_protocol.hpp" +#include "network/protocols/game_events_protocol.hpp" +#include "modes/world.hpp" + +#include "karts/controller/controller.hpp" + +NetworkWorld::NetworkWorld() +{ + m_running = false; + m_has_run = false; +} + +NetworkWorld::~NetworkWorld() +{ +} + +void NetworkWorld::update(float dt) +{ + if (!m_has_run) + m_has_run = true; + SynchronizationProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (protocol) // if this protocol exists, that's that we play online + { + Log::debug("NetworkWorld", "Coutdown value is %f", protocol->getCountdown()); + if (protocol->getCountdown() > 0.0) + { + return; + } + World::getWorld()->setNetworkWorld(true); + } + World::getWorld()->updateWorld(dt); + if (World::getWorld()->getPhase() >= WorldStatus::RESULT_DISPLAY_PHASE) // means it's the end + { + // consider the world finished. + stop(); + Log::info("NetworkWorld", "The game is considered finish."); + } +} + +void NetworkWorld::start() +{ + m_running = true; +} + +void NetworkWorld::stop() +{ + m_running = false; +} + +bool NetworkWorld::isRaceOver() +{ + if (!World::getWorld()) + return false; + return (World::getWorld()->getPhase() > WorldStatus::RACE_PHASE); +} + +void NetworkWorld::collectedItem(Item *item, AbstractKart *kart) +{ + assert(NetworkManager::getInstance()->isServer()); // this is only called in the server + GameEventsProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_GAME_EVENTS)); + protocol->collectedItem(item,kart); +} + +void NetworkWorld::controllerAction(Controller* controller, PlayerAction action, int value) +{ + ControllerEventsProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_CONTROLLER_EVENTS)); + if (protocol) + protocol->controllerAction(controller, action, value); +} + diff --git a/src/network/network_world.hpp b/src/network/network_world.hpp new file mode 100644 index 000000000..7cbd8899c --- /dev/null +++ b/src/network/network_world.hpp @@ -0,0 +1,62 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file network_world.hpp + */ + +#ifndef NETWORK_WORLD_HPP +#define NETWORK_WORLD_HPP + +#include "network/singleton.hpp" +#include "input/input.hpp" +#include + +class Controller; +class KartUpdateProtocol; +class AbstractKart; +class Item; + +/*! \brief Manages the world updates during an online game + * This function's update is to be called instead of the normal World update +*/ +class NetworkWorld : public Singleton +{ + friend class Singleton; + public: + void update(float dt); + + void start(); + void stop(); + bool isRunning() { return m_running; } + bool isRaceOver(); + + void collectedItem(Item *item, AbstractKart *kart); + void controllerAction(Controller* controller, PlayerAction action, int value); + + std::string m_self_kart; + protected: + bool m_running; + float m_race_time; + bool m_has_run; + + private: + NetworkWorld(); + virtual ~NetworkWorld(); +}; + +#endif // NETWORK_WORLD_HPP diff --git a/src/network/protocol.cpp b/src/network/protocol.cpp new file mode 100644 index 000000000..d71d8c265 --- /dev/null +++ b/src/network/protocol.cpp @@ -0,0 +1,99 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocol.hpp" + +#include "network/protocol_manager.hpp" +#include "network/network_manager.hpp" + +Protocol::Protocol(CallbackObject* callback_object, PROTOCOL_TYPE type) +{ + m_callback_object = callback_object; + m_type = type; +} + +Protocol::~Protocol() +{ +} + +void Protocol::pause() +{ + m_listener->requestPause(this); +} +void Protocol::unpause() +{ + m_listener->requestUnpause(this); +} + +void Protocol::kill() +{ +} + +void Protocol::setListener(ProtocolManager* listener) +{ + m_listener = listener; +} + +PROTOCOL_TYPE Protocol::getProtocolType() +{ + return m_type; +} + +bool Protocol::checkDataSizeAndToken(Event* event, int minimum_size) +{ + NetworkString data = event->data(); + if (data.size() < minimum_size || data[0] != 4) + { + Log::warn("Protocol", "Receiving a badly " + "formated message. Size is %d and first byte %d", + data.size(), data[0]); + return false; + } + STKPeer* peer = *(event->peer); + uint32_t token = data.gui32(1); + if (token != peer->getClientServerToken()) + { + Log::warn("Protocol", "Peer sending bad token. Request " + "aborted."); + return false; + } + return true; +} + +bool Protocol::isByteCorrect(Event* event, int byte_nb, int value) +{ + NetworkString data = event->data(); + if (data[byte_nb] != value) + { + Log::info("Protocol", "Bad byte at pos %d. %d " + "should be %d", byte_nb, data[byte_nb], value); + return false; + } + return true; +} + +void Protocol::sendMessageToPeersChangingToken(NetworkString prefix, NetworkString message) +{ + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + prefix.ai8(4).ai32(peers[i]->getClientServerToken()); + prefix += message; + m_listener->sendMessage(this, peers[i], prefix); + } +} diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp new file mode 100644 index 000000000..4fab04112 --- /dev/null +++ b/src/network/protocol.hpp @@ -0,0 +1,128 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file protocol.hpp + * \brief Generic protocols declarations. + */ + +#ifndef PROTOCOL_HPP +#define PROTOCOL_HPP + +#include "network/event.hpp" +#include "network/types.hpp" +#include "utils/types.hpp" + +class ProtocolManager; + +/** \enum PROTOCOL_TYPE + * \brief The types that protocols can have. This is used to select which protocol receives which event. + * \ingroup network + */ +enum PROTOCOL_TYPE +{ + PROTOCOL_NONE = 0, //!< No protocol type assigned. + PROTOCOL_CONNECTION = 1, //!< Protocol that deals with client-server connection. + PROTOCOL_LOBBY_ROOM = 2, //!< Protocol that is used during the lobby room phase. + PROTOCOL_START_GAME = 3, //!< Protocol used when starting the game. + PROTOCOL_SYNCHRONIZATION = 4,//! +#include +#include +#include + +void* protocolManagerUpdate(void* data) +{ + ProtocolManager* manager = static_cast(data); + while(manager && !manager->exit()) + { + manager->update(); + StkTime::sleep(2); + } + return NULL; +} +void* protocolManagerAsynchronousUpdate(void* data) +{ + ProtocolManager* manager = static_cast(data); + manager->m_asynchronous_thread_running = true; + while(manager && !manager->exit()) + { + manager->asynchronousUpdate(); + StkTime::sleep(2); + } + manager->m_asynchronous_thread_running = false; + return NULL; +} + +ProtocolManager::ProtocolManager() +{ + pthread_mutex_init(&m_events_mutex, NULL); + pthread_mutex_init(&m_protocols_mutex, NULL); + pthread_mutex_init(&m_asynchronous_protocols_mutex, NULL); + pthread_mutex_init(&m_requests_mutex, NULL); + pthread_mutex_init(&m_id_mutex, NULL); + pthread_mutex_init(&m_exit_mutex, NULL); + m_next_protocol_id = 0; + + + pthread_mutex_lock(&m_exit_mutex); // will let the update function run + /// FIXME used on server because mainloop never running + /*if (NetworkManager::getInstance()->isServer()) + { + m_update_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_update_thread, NULL, protocolManagerUpdate, this); + }*/ + // always run this one + m_asynchronous_update_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_asynchronous_update_thread, NULL, protocolManagerAsynchronousUpdate, this); +} + +ProtocolManager::~ProtocolManager() +{ +} + + +void ProtocolManager::abort() +{ + pthread_mutex_unlock(&m_exit_mutex); // will stop the update function + pthread_join(*m_asynchronous_update_thread, NULL); // wait the thread to finish + pthread_mutex_lock(&m_events_mutex); + pthread_mutex_lock(&m_protocols_mutex); + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + pthread_mutex_lock(&m_requests_mutex); + pthread_mutex_lock(&m_id_mutex); + for (unsigned int i = 0; i < m_protocols.size() ; i++) + delete m_protocols[i].protocol; + for (unsigned int i = 0; i < m_events_to_process.size() ; i++) + delete m_events_to_process[i].event; + m_protocols.clear(); + m_requests.clear(); + m_events_to_process.clear(); + pthread_mutex_unlock(&m_events_mutex); + pthread_mutex_unlock(&m_protocols_mutex); + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); + pthread_mutex_unlock(&m_requests_mutex); + pthread_mutex_unlock(&m_id_mutex); + + pthread_mutex_destroy(&m_events_mutex); + pthread_mutex_destroy(&m_protocols_mutex); + pthread_mutex_destroy(&m_asynchronous_protocols_mutex); + pthread_mutex_destroy(&m_requests_mutex); + pthread_mutex_destroy(&m_id_mutex); + pthread_mutex_destroy(&m_exit_mutex); +} + +void ProtocolManager::notifyEvent(Event* event) +{ + pthread_mutex_lock(&m_events_mutex); + Event* event2 = new Event(*event); + // register protocols that will receive this event + std::vector protocols_ids; + PROTOCOL_TYPE searchedProtocol = PROTOCOL_NONE; + if (event2->type == EVENT_TYPE_MESSAGE) + { + if (event2->data().size() > 0) + { + searchedProtocol = (PROTOCOL_TYPE)(event2->data()[0]); + event2->removeFront(1); + } + else + { + Log::warn("ProtocolManager", "Not enough data."); + } + } + if (event2->type == EVENT_TYPE_CONNECTED) + { + searchedProtocol = PROTOCOL_CONNECTION; + } + Log::verbose("ProtocolManager", "Received event for protocols of type %d", searchedProtocol); + pthread_mutex_lock(&m_protocols_mutex); + for (unsigned int i = 0; i < m_protocols.size() ; i++) + { + if (m_protocols[i].protocol->getProtocolType() == searchedProtocol || event2->type == EVENT_TYPE_DISCONNECTED) // pass data to protocols even when paused + { + protocols_ids.push_back(m_protocols[i].id); + } + } + pthread_mutex_unlock(&m_protocols_mutex); + if (searchedProtocol == PROTOCOL_NONE) // no protocol was aimed, show the msg to debug + { + Log::debug("ProtocolManager", "NO PROTOCOL : Message is \"%s\"", event2->data().c_str()); + } + + if (protocols_ids.size() != 0) + { + EventProcessingInfo epi; + epi.arrival_time = (double)StkTime::getTimeSinceEpoch(); + epi.event = event2; + epi.protocols_ids = protocols_ids; + m_events_to_process.push_back(epi); // add the event to the queue + } + else + Log::warn("ProtocolManager", "Received an event for %d that has no destination protocol.", searchedProtocol); + pthread_mutex_unlock(&m_events_mutex); +} + +void ProtocolManager::sendMessage(Protocol* sender, const NetworkString& message, bool reliable) +{ + NetworkString newMessage; + newMessage.ai8(sender->getProtocolType()); // add one byte to add protocol type + newMessage += message; + NetworkManager::getInstance()->sendPacket(newMessage, reliable); +} + +void ProtocolManager::sendMessage(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable) +{ + NetworkString newMessage; + newMessage.ai8(sender->getProtocolType()); // add one byte to add protocol type + newMessage += message; + NetworkManager::getInstance()->sendPacket(peer, newMessage, reliable); +} +void ProtocolManager::sendMessageExcept(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable) +{ + NetworkString newMessage; + newMessage.ai8(sender->getProtocolType()); // add one byte to add protocol type + newMessage += message; + NetworkManager::getInstance()->sendPacketExcept(peer, newMessage, reliable); +} + +uint32_t ProtocolManager::requestStart(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + ProtocolInfo info; + info.protocol = protocol; + info.state = PROTOCOL_STATE_RUNNING; + assignProtocolId(&info); // assign a unique id to the protocol. + req.protocol_info = info; + req.type = PROTOCOL_REQUEST_START; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); + + return info.id; +} + +void ProtocolManager::requestStop(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_STOP; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestPause(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_PAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestUnpause(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_UNPAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestTerminate(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_TERMINATE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + // check that the request does not already exist : + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.protocol == protocol) + { + pthread_mutex_unlock(&m_requests_mutex); + return; + } + } + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::startProtocol(ProtocolInfo protocol) +{ + // add the protocol to the protocol vector so that it's updated + pthread_mutex_lock(&m_protocols_mutex); + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + Log::info("ProtocolManager", "A %s protocol with id=%u has been started. There are %ld protocols running.", typeid(*protocol.protocol).name(), protocol.id, m_protocols.size()+1); + m_protocols.push_back(protocol); + // setup the protocol and notify it that it's started + protocol.protocol->setListener(this); + protocol.protocol->setup(); + pthread_mutex_unlock(&m_protocols_mutex); + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); +} +void ProtocolManager::stopProtocol(ProtocolInfo protocol) +{ + +} +void ProtocolManager::pauseProtocol(ProtocolInfo protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_RUNNING) + { + m_protocols[i].state = PROTOCOL_STATE_PAUSED; + m_protocols[i].protocol->pause(); + } + } +} +void ProtocolManager::unpauseProtocol(ProtocolInfo protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_PAUSED) + { + m_protocols[i].state = PROTOCOL_STATE_RUNNING; + m_protocols[i].protocol->unpause(); + } + } +} +void ProtocolManager::protocolTerminated(ProtocolInfo protocol) +{ + pthread_mutex_lock(&m_protocols_mutex); // be sure that noone accesses the protocols vector while we erase a protocol + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + int offset = 0; + std::string protocol_type = typeid(*protocol.protocol).name(); + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i-offset].protocol == protocol.protocol) + { + delete m_protocols[i].protocol; + m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); + offset++; + } + } + Log::info("ProtocolManager", "A %s protocol has been terminated. There are %ld protocols running.", protocol_type.c_str(), m_protocols.size()); + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); + pthread_mutex_unlock(&m_protocols_mutex); +} + +bool ProtocolManager::propagateEvent(EventProcessingInfo* event, bool synchronous) +{ + int index = 0; + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (event->protocols_ids[index] == m_protocols[i].id) + { + bool result = false; + if (synchronous) + result = m_protocols[i].protocol->notifyEvent(event->event); + else + result = m_protocols[i].protocol->notifyEventAsynchronous(event->event); + if (result) + event->protocols_ids.pop_back(); + else + index++; + } + } + if (event->protocols_ids.size() == 0 || (StkTime::getTimeSinceEpoch()-event->arrival_time) >= TIME_TO_KEEP_EVENTS) + { + // because we made a copy of the event + delete event->event->peer; // no more need of that + delete event->event; + return true; + } + return false; +} + +void ProtocolManager::update() +{ + // before updating, notice protocols that they have received events + pthread_mutex_lock(&m_events_mutex); // secure threads + int size = m_events_to_process.size(); + int offset = 0; + for (int i = 0; i < size; i++) + { + bool result = propagateEvent(&m_events_to_process[i+offset], true); + if (result) + { + m_events_to_process.erase(m_events_to_process.begin()+i+offset,m_events_to_process.begin()+i+offset+1); + offset --; + } + } + pthread_mutex_unlock(&m_events_mutex); // release the mutex + // now update all protocols + pthread_mutex_lock(&m_protocols_mutex); + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].state == PROTOCOL_STATE_RUNNING) + m_protocols[i].protocol->update(); + } + pthread_mutex_unlock(&m_protocols_mutex); +} + +void ProtocolManager::asynchronousUpdate() +{ + // before updating, notice protocols that they have received information + pthread_mutex_lock(&m_events_mutex); // secure threads + int size = m_events_to_process.size(); + int offset = 0; + for (int i = 0; i < size; i++) + { + bool result = propagateEvent(&m_events_to_process[i+offset], false); + if (result) + { + m_events_to_process.erase(m_events_to_process.begin()+i+offset,m_events_to_process.begin()+i+offset+1); + offset --; + } + } + pthread_mutex_unlock(&m_events_mutex); // release the mutex + + // now update all protocols that need to be updated in asynchronous mode + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].state == PROTOCOL_STATE_RUNNING) + m_protocols[i].protocol->asynchronousUpdate(); + } + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); + + // process queued events for protocols + // these requests are asynchronous + pthread_mutex_lock(&m_requests_mutex); + for (unsigned int i = 0; i < m_requests.size(); i++) + { + switch (m_requests[i].type) + { + case PROTOCOL_REQUEST_START: + startProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_STOP: + stopProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_PAUSE: + pauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_UNPAUSE: + unpauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_TERMINATE: + protocolTerminated(m_requests[i].protocol_info); + break; + } + } + m_requests.clear(); + pthread_mutex_unlock(&m_requests_mutex); +} + +int ProtocolManager::runningProtocolsCount() +{ + return m_protocols.size(); +} + +PROTOCOL_STATE ProtocolManager::getProtocolState(uint32_t id) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].id == id) // we know a protocol with that id + return m_protocols[i].state; // return its state + } + // the protocol isn't running right now + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.id == id) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // else, it's already finished +} + +PROTOCOL_STATE ProtocolManager::getProtocolState(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) // the protocol is known + return m_protocols[i].state; // return its state + } + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.protocol == protocol) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // we don't know this protocol at all, it's finished +} + +uint32_t ProtocolManager::getProtocolID(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) + return m_protocols[i].id; + } + return 0; +} + +Protocol* ProtocolManager::getProtocol(uint32_t id) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].id == id) + return m_protocols[i].protocol; + } + return NULL; +} + +Protocol* ProtocolManager::getProtocol(PROTOCOL_TYPE type) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol->getProtocolType() == type) + return m_protocols[i].protocol; + } + return NULL; +} + +bool ProtocolManager::isServer() +{ + return NetworkManager::getInstance()->isServer(); +} + +int ProtocolManager::exit() +{ + switch(pthread_mutex_trylock(&m_exit_mutex)) { + case 0: /* if we got the lock, unlock and return 1 (true) */ + pthread_mutex_unlock(&m_exit_mutex); + return 1; + case EBUSY: /* return 0 (false) if the mutex was locked */ + return 0; + } + return 1; +} + +void ProtocolManager::assignProtocolId(ProtocolInfo* protocol_info) +{ + pthread_mutex_lock(&m_id_mutex); + protocol_info->id = m_next_protocol_id; + m_next_protocol_id++; + pthread_mutex_unlock(&m_id_mutex); +} + + diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp new file mode 100644 index 000000000..b6ae38251 --- /dev/null +++ b/src/network/protocol_manager.hpp @@ -0,0 +1,334 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file protocol_manager.hpp + * \brief Contains structures and enumerations related to protocol management. + */ + +#ifndef PROTOCOL_MANAGER_HPP +#define PROTOCOL_MANAGER_HPP + +#include "network/singleton.hpp" +#include "network/event.hpp" +#include "network/network_string.hpp" +#include "network/protocol.hpp" +#include "utils/types.hpp" + +#include + +#define TIME_TO_KEEP_EVENTS 1.0 + +/*! + * \enum PROTOCOL_STATE + * \brief Defines the three states that a protocol can have. + */ +enum PROTOCOL_STATE +{ + PROTOCOL_STATE_RUNNING, //!< The protocol is being updated everytime. + PROTOCOL_STATE_PAUSED, //!< The protocol is paused. + PROTOCOL_STATE_TERMINATED //!< The protocol is terminated/does not exist. +}; + +/*! + * \enum PROTOCOL_REQUEST_TYPE + * \brief Defines actions that can be done about protocols. + * This enum is used essentially to keep the manager thread-safe and + * to avoid protocols modifying directly their state. + */ +enum PROTOCOL_REQUEST_TYPE +{ + PROTOCOL_REQUEST_START, //!< Start a protocol + PROTOCOL_REQUEST_STOP, //!< Stop a protocol + PROTOCOL_REQUEST_PAUSE, //!< Pause a protocol + PROTOCOL_REQUEST_UNPAUSE, //!< Unpause a protocol + PROTOCOL_REQUEST_TERMINATE //!< Terminate a protocol +}; + +/*! +* \struct ProtocolInfo +* \brief Stores the information needed to manage protocols +*/ +typedef struct ProtocolInfo +{ + PROTOCOL_STATE state; //!< The state of the protocol + Protocol* protocol; //!< A pointer to the protocol + uint32_t id; //!< The unique id of the protocol +} ProtocolInfo; + +/*! +* \struct ProtocolRequest +* \brief Represents a request to do an action about a protocol. +*/ +typedef struct ProtocolRequest +{ + PROTOCOL_REQUEST_TYPE type; //!< The type of request + ProtocolInfo protocol_info; //!< The concerned protocol information +} ProtocolRequest; + +/*! \struct ProtocolRequest + * \brief Used to pass the event to protocols that need it + */ +typedef struct EventProcessingInfo +{ + Event* event; + double arrival_time; + std::vector protocols_ids; +} EventProcessingInfo; + +/*! + * \class ProtocolManager + * \brief Manages the protocols at runtime. + * + * This class is in charge of storing and managing protocols. + * It is a singleton as there can be only one protocol manager per game + * instance. Any game object that wants to start a protocol must create a + * protocol and give it to this singleton. The protocols are updated in a + * special thread, to ensure that they are processed independently from the + * frames per second. Then, the management of protocols is thread-safe: any + * object can start/pause/stop protocols whithout problems. + */ +class ProtocolManager : public Singleton +{ + friend class Singleton; + friend void* protocolManagerAsynchronousUpdate(void* data); + public: + + /*! \brief Stops the protocol manager. */ + virtual void abort(); + /*! + * \brief Function that processes incoming events. + * This function is called by the network manager each time there is an + * incoming packet. + */ + virtual void notifyEvent(Event* event); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessage(Protocol* sender, const NetworkString& message, bool reliable = true); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessage(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable = true); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessageExcept(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable = true); + + /*! + * \brief Asks the manager to start a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to start + * \return The unique id of the protocol that is being started. + */ + virtual uint32_t requestStart(Protocol* protocol); + /*! + * \brief Asks the manager to stop a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to stop + */ + virtual void requestStop(Protocol* protocol); + /*! + * \brief Asks the manager to pause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to pause + */ + virtual void requestPause(Protocol* protocol); + /*! + * \brief Asks the manager to unpause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to unpause + */ + virtual void requestUnpause(Protocol* protocol); + /*! + * \brief Notifies the manager that a protocol is terminated. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol that is finished + */ + virtual void requestTerminate(Protocol* protocol); + + /*! + * \brief Updates the manager. + * + * This function processes the events queue, notifies the concerned + * protocols that they have events to process. Then ask all protocols + * to update themselves. Finally processes stored requests about + * starting, stoping, pausing etc... protocols. + * This function is called by the main loop. + * This function IS FPS-dependant. + */ + virtual void update(); + /*! + * \brief Updates the manager. + * + * This function processes the events queue, notifies the concerned + * protocols that they have events to process. Then ask all protocols + * to update themselves. Finally processes stored requests about + * starting, stoping, pausing etc... protocols. + * This function is called in a thread. + * This function IS NOT FPS-dependant. + */ + virtual void asynchronousUpdate(); + + /*! + * \brief Get the number of protocols running. + * \return The number of protocols that are actually running. + */ + virtual int runningProtocolsCount(); + /*! + * \brief Get the state of a protocol using its id. + * \param id : The id of the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(uint32_t id); + /*! + * \brief Get the state of a protocol using a pointer on it. + * \param protocol : A pointer to the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(Protocol* protocol); + /*! + * \brief Get the id of a protocol. + * \param protocol : A pointer to the protocol you seek the id. + * \return The id of the protocol pointed by the protocol parameter. + */ + virtual uint32_t getProtocolID(Protocol* protocol); + + /*! + * \brief Get a protocol using its id. + * \param id : Unique ID of the seek protocol. + * \return The protocol that has the ID id. + */ + virtual Protocol* getProtocol(uint32_t id); + /*! + * \brief Get a protocol using its type. + * \param type : The type of the protocol. + * \return The protocol that matches the given type. + */ + virtual Protocol* getProtocol(PROTOCOL_TYPE type); + + /*! \brief Know whether the app is a server. + * \return True if this application is in server mode, false elseway. + */ + bool isServer(); + + /*! \brief Tells if we need to stop the update thread. */ + int exit(); + + protected: + // protected functions + /*! + * \brief Constructor + */ + ProtocolManager(); + /*! + * \brief Destructor + */ + virtual ~ProtocolManager(); + /*! + * \brief Assign an id to a protocol. + * This function will assign m_next_protocol_id as the protocol id. + * This id starts at 0 at the beginning and is increased by 1 each time + * a protocol starts. + * \param protocol_info : The protocol info that needs an id. + */ + void assignProtocolId(ProtocolInfo* protocol_info); + + /*! + * \brief Starts a protocol. + * Add the protocol info to the m_protocols vector. + * \param protocol : ProtocolInfo to start. + */ + virtual void startProtocol(ProtocolInfo protocol); + /*! + * \brief Stops a protocol. + * Coes nothing. Noone can stop running protocols for now. + * \param protocol : ProtocolInfo to stop. + */ + virtual void stopProtocol(ProtocolInfo protocol); + /*! + * \brief Pauses a protocol. + * Pauses a protocol and tells it that it's being paused. + * \param protocol : ProtocolInfo to pause. + */ + virtual void pauseProtocol(ProtocolInfo protocol); + /*! + * \brief Unpauses a protocol. + * Unpauses a protocol and notifies it. + * \param protocol : ProtocolInfo to unpause. + */ + virtual void unpauseProtocol(ProtocolInfo protocol); + /*! + * \brief Notes that a protocol is terminated. + * Remove a protocol from the protocols vector. + * \param protocol : ProtocolInfo concerned. + */ + virtual void protocolTerminated(ProtocolInfo protocol); + + bool propagateEvent(EventProcessingInfo* event, bool synchronous); + + // protected members + /*! + * \brief Contains the running protocols. + * This stores the protocols that are either running or paused, their + * state and their unique id. + */ + std::vector m_protocols; + /*! + * \brief Contains the network events to pass to protocols. + */ + std::vector m_events_to_process; + /*! + * \brief Contains the requests to start/stop etc... protocols. + */ + std::vector m_requests; + /*! \brief The next id to assign to a protocol. + * This value is incremented by 1 each time a protocol is started. + * If a protocol has an id lower than this value, it means that it have + * been formerly started. + */ + uint32_t m_next_protocol_id; + + // mutexes: + /*! Used to ensure that the event queue is used thread-safely. */ + pthread_mutex_t m_events_mutex; + /*! Used to ensure that the protocol vector is used thread-safely. */ + pthread_mutex_t m_protocols_mutex; + /*! Used to ensure that the protocol vector is used thread-safely. */ + pthread_mutex_t m_asynchronous_protocols_mutex; + /*! Used to ensure that the request vector is used thread-safely. */ + pthread_mutex_t m_requests_mutex; + /*! Used to ensure that the protocol id is used in a thread-safe way.*/ + pthread_mutex_t m_id_mutex; + /*! Used when need to quit.*/ + pthread_mutex_t m_exit_mutex; + + /*! Update thread.*/ + pthread_t* m_update_thread; + /*! Asynchronous update thread.*/ + pthread_t* m_asynchronous_update_thread; + /*! True if the thread is running. */ + bool m_asynchronous_thread_running; + +}; + +#endif // PROTOCOL_MANAGER_HPP diff --git a/src/network/protocols/client_lobby_room_protocol.cpp b/src/network/protocols/client_lobby_room_protocol.cpp new file mode 100644 index 000000000..a6abcb51a --- /dev/null +++ b/src/network/protocols/client_lobby_room_protocol.cpp @@ -0,0 +1,806 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/client_lobby_room_protocol.hpp" + +#include "network/network_manager.hpp" +#include "network/protocols/start_game_protocol.hpp" +#include "network/network_world.hpp" + +#include "modes/world_with_rank.hpp" +#include "online/current_user.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/network_kart_selection.hpp" +#include "utils/log.hpp" + +ClientLobbyRoomProtocol::ClientLobbyRoomProtocol(const TransportAddress& server_address) + : LobbyRoomProtocol(NULL) +{ + m_server_address = server_address; + m_server = NULL; +} + +//----------------------------------------------------------------------------- + +ClientLobbyRoomProtocol::~ClientLobbyRoomProtocol() +{ +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::setup() +{ + m_setup = NetworkManager::getInstance()->setupNewGame(); // create a new setup + m_setup->getRaceConfig()->setPlayerCount(16); //FIXME : this has to be changed when logging into the server + m_state = NONE; +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::requestKartSelection(std::string kart_name) +{ + NetworkString request; + // 0x02 : kart selection request, size_token (4), token, size kart name, kart name + request.ai8(0x02).ai8(4).ai32(m_server->getClientServerToken()).ai8(kart_name.size()).as(kart_name); + m_listener->sendMessage(this, request, true); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::voteMajor(uint8_t major) +{ + NetworkString request; + // 0xc0 : major vote, size_token (4), token, size major(1),major + request.ai8(0xc0).ai8(4).ai32(m_server->getClientServerToken()).ai8(1).ai8(major); + m_listener->sendMessage(this, request, true); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::voteRaceCount(uint8_t count) +{ + NetworkString request; + // 0xc0 : race count vote, size_token (4), token, size race count(1), count + request.ai8(0xc1).ai8(4).ai32(m_server->getClientServerToken()).ai8(1).ai8(count); + m_listener->sendMessage(this, request, true); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::voteMinor(uint8_t minor) +{ + NetworkString request; + // 0xc0 : minor vote, size_token (4), token, size minor(1),minor + request.ai8(0xc2).ai8(4).ai32(m_server->getClientServerToken()).ai8(1).ai8(minor); + m_listener->sendMessage(this, request, true); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::voteTrack(std::string track, uint8_t track_nb) +{ + NetworkString request; + // 0xc0 : major vote, size_token (4), token, size track, track, size #track, #track + request.ai8(0xc3).ai8(4).ai32(m_server->getClientServerToken()).ai8(track.size()).as(track).ai8(1).ai8(track_nb); + m_listener->sendMessage(this, request, true); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::voteReversed(bool reversed, uint8_t track_nb) +{ + NetworkString request; + // 0xc0 : major vote, size_token (4), token, size reversed(1),reversed, size #track, #track + request.ai8(0xc4).ai8(4).ai32(m_server->getClientServerToken()).ai8(1).ai8(reversed).ai8(1).ai8(track_nb); + m_listener->sendMessage(this, request, true); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::voteLaps(uint8_t laps, uint8_t track_nb) +{ + NetworkString request; + // 0xc0 : major vote, size_token (4), token, size laps(1),laps, size #track, #track + request.ai8(0xc5).ai8(4).ai32(m_server->getClientServerToken()).ai8(1).ai8(laps).ai8(1).ai8(track_nb); + m_listener->sendMessage(this, request, true); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::leave() +{ + m_server->disconnect(); + m_server_address.ip = 0; + m_server_address.port = 0; +} + +//----------------------------------------------------------------------------- + +bool ClientLobbyRoomProtocol::notifyEvent(Event* event) +{ + assert(m_setup); // assert that the setup exists + if (event->type == EVENT_TYPE_MESSAGE) + { + NetworkString data = event->data(); + assert(data.size()); // assert that data isn't empty + uint8_t message_type = data[0]; + if (message_type != 0x03 && + message_type != 0x06) + return false; // don't treat the event + + event->removeFront(1); + Log::info("ClientLobbyRoomProtocol", "Synchronous message of type %d", message_type); + if (message_type == 0x03) // kart selection update + kartSelectionUpdate(event); + else if (message_type == 0x06) // end of race + raceFinished(event); + + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool ClientLobbyRoomProtocol::notifyEventAsynchronous(Event* event) +{ + assert(m_setup); // assert that the setup exists + if (event->type == EVENT_TYPE_MESSAGE) + { + NetworkString data = event->data(); + assert(data.size()); // assert that data isn't empty + uint8_t message_type = data[0]; + if (message_type == 0x03 || + message_type == 0x06) + return false; // don't treat the event + + event->removeFront(1); + Log::info("ClientLobbyRoomProtocol", "Asynchronous message of type %d", message_type); + if (message_type == 0x01) // new player connected + newPlayer(event); + else if (message_type == 0x02) // player disconnected + disconnectedPlayer(event); + else if (message_type == 0x04) // start race + startGame(event); + else if (message_type == 0x05) // start selection phase + startSelection(event); + else if (message_type == 0x80) // connection refused + connectionRefused(event); + else if (message_type == 0x81) // connection accepted + connectionAccepted(event); + else if (message_type == 0x82) // kart selection refused + kartSelectionRefused(event); + else if (message_type == 0xc0) // vote for major mode + playerMajorVote(event); + else if (message_type == 0xc1) // vote for race count + playerRaceCountVote(event); + else if (message_type == 0xc2) // vote for minor mode + playerMinorVote(event); + else if (message_type == 0xc3) // vote for track + playerTrackVote(event); + else if (message_type == 0xc4) // vote for reversed mode + playerReversedVote(event); + else if (message_type == 0xc5) // vote for laps + playerLapsVote(event); + + return true; + } // message + else if (event->type == EVENT_TYPE_CONNECTED) + { + return true; + } // connection + else if (event->type == EVENT_TYPE_DISCONNECTED) // means we left essentially + { + NetworkManager::getInstance()->removePeer(m_server); + m_server = NULL; + NetworkManager::getInstance()->disconnected(); + m_listener->requestTerminate(this); + NetworkManager::getInstance()->reset(); + NetworkManager::getInstance()->removePeer(*event->peer); // prolly the same as m_server + return true; + } // disconnection + return false; +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::update() +{ + switch (m_state) + { + case NONE: + if (NetworkManager::getInstance()->isConnectedTo(m_server_address)) + { + m_state = LINKED; + } + break; + case LINKED: + { + NetworkString ns; + // 1 (connection request), 4 (size of id), global id + ns.ai8(1).ai8(4).ai32(Online::CurrentUser::get()->getID()); + m_listener->sendMessage(this, ns); + m_state = REQUESTING_CONNECTION; + } + break; + case REQUESTING_CONNECTION: + break; + case CONNECTED: + break; + case KART_SELECTION: + { + NetworkKartSelectionScreen* screen = NetworkKartSelectionScreen::getInstance(); + StateManager::get()->pushScreen(screen); + m_state = SELECTING_KARTS; + } + break; + case SELECTING_KARTS: + break; + case PLAYING: + { + if (NetworkWorld::getInstance()->isRaceOver()) // race is now over, kill race protocols and return to connected state + { + Log::info("ClientLobbyRoomProtocol", "Game finished."); + m_state = RACE_FINISHED; + } + } + break; + case RACE_FINISHED: + break; + case DONE: + m_state = EXITING; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a new player is connected to the server + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 + * ------------------------------------------------ + * Size | 1 | 4 | 1 | 1 | + * Data | 4 | player global id | 1 | 0 <= race id < 16 | + * ------------------------------------------------ + */ +void ClientLobbyRoomProtocol::newPlayer(Event* event) +{ + NetworkString data = event->data(); + if (data.size() != 7 || data[0] != 4 || data[5] != 1) // 7 bytes remains now + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a new player wasn't formated as expected."); + return; + } + + uint32_t global_id = data.gui32(1); + uint8_t race_id = data.gui8(6); + + if (global_id == Online::CurrentUser::get()->getID()) + { + Log::error("ClientLobbyRoomProtocol", "The server notified me that i'm a new player in the room (not normal)."); + } + else if (m_setup->getProfile(race_id) == NULL || m_setup->getProfile(global_id) == NULL) + { + Log::verbose("ClientLobbyRoomProtocol", "New player connected."); + NetworkPlayerProfile* profile = new NetworkPlayerProfile(); + profile->kart_name = ""; + profile->race_id = race_id; + profile->user_profile = new Online::Profile(global_id, ""); + m_setup->addPlayer(profile); + } + else + { + Log::error("ClientLobbyRoomProtocol", "One of the player notified in the list is myself."); + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a new player is disconnected + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 + * ------------------------- + * Size | 1 | 1 | + * Data | 1 | 0 <= race id < 16 | + * ------------------------- + */ +void ClientLobbyRoomProtocol::disconnectedPlayer(Event* event) +{ + NetworkString data = event->data(); + if (data.size() != 2 || data[0] != 1) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a new player wasn't formated as expected."); + return; + } + uint8_t id = data[1]; + if (m_setup->removePlayer(id)) + { + Log::info("ClientLobbyRoomProtocol", "Peer removed successfully."); + } + else + { + Log::error("ClientLobbyRoomProtocol", "The disconnected peer wasn't known."); + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server accepts the connection. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 3 7 8 12 + * ---------------------------------------------------------- + * Size | 1 | 1 | 1 | 4 | 1 | 4 | + * Data | 1 | 0 <= race id < 16 | 4 | priv token | 4 | global id | + * ---------------------------------------------------------- + */ +void ClientLobbyRoomProtocol::connectionAccepted(Event* event) +{ + NetworkString data = event->data(); + if (data.size() < 12 || data[0] != 1 || data[2] != 4 || data[7] != 4) // 12 bytes remains now + { + Log::error("ClientLobbyRoomProtocol", "A message notifying an accepted connection wasn't formated as expected."); + return; + } + STKPeer* peer = *(event->peer); + + uint32_t global_id = data.gui32(8); + if (global_id == Online::CurrentUser::get()->getID()) + { + Log::info("ClientLobbyRoomProtocol", "The server accepted the connection."); + + // self profile + NetworkPlayerProfile* profile = new NetworkPlayerProfile(); + profile->kart_name = ""; + profile->race_id = data.gui8(1); + profile->user_profile = Online::CurrentUser::get()->getProfile(); + m_setup->addPlayer(profile); + // connection token + uint32_t token = data.gui32(3); + peer->setClientServerToken(token); + // add all players + data.removeFront(12); // remove the 12 first bytes + int remaining = data.size(); + if (remaining%7 != 0) + { + Log::error("ClientLobbyRoomProtocol", "ConnectionAccepted : Error in the server list"); + } + remaining /= 7; + for (int i = 0; i < remaining; i++) + { + if (data[0] != 1 || data[2] != 4) + Log::error("ClientLobbyRoomProtocol", "Bad format in players list."); + + uint8_t race_id = data[1]; + uint32_t global_id = data.gui32(3); + Online::Profile* new_user = new Online::Profile(global_id, ""); + + NetworkPlayerProfile* profile2 = new NetworkPlayerProfile(); + profile2->race_id = race_id; + profile2->user_profile = new_user; + profile2->kart_name = ""; + m_setup->addPlayer(profile2); + data.removeFront(7); + } + + // add self + m_server = *(event->peer); + m_state = CONNECTED; + } + else + Log::info("ClientLobbyRoomProtocol", "Failure during the connection acceptation process."); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server refuses the connection. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 + * -------------------- + * Size | 1 | 1 | + * Data | 1 | refusal code | + * -------------------- + */ +void ClientLobbyRoomProtocol::connectionRefused(Event* event) +{ + NetworkString data = event->data(); + if (data.size() != 2 || data[0] != 1) // 2 bytes remains now + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a refused connection wasn't formated as expected."); + return; + } + + switch (data[1]) // the second byte + { + case 0: + Log::info("ClientLobbyRoomProtocol", "Connection refused : too many players."); + break; + case 1: + Log::info("ClientLobbyRoomProtocol", "Connection refused : banned."); + break; + default: + Log::info("ClientLobbyRoomProtocol", "Connection refused."); + break; + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server refuses the kart selection request. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 + * -------------------- + * Size | 1 | 1 | + * Data | 1 | refusal code | + * -------------------- + */ +void ClientLobbyRoomProtocol::kartSelectionRefused(Event* event) +{ + NetworkString data = event->data(); + if (data.size() != 2 || data[0] != 1) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a refused kart selection wasn't formated as expected."); + return; + } + + switch (data[1]) // the error code + { + case 0: + Log::info("ClientLobbyRoomProtocol", "Kart selection refused : already taken."); + break; + case 1: + Log::info("ClientLobbyRoomProtocol", "Kart selection refused : not available."); + break; + default: + Log::info("ClientLobbyRoomProtocol", "Kart selection refused."); + break; + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server tells to update a player's kart. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 3 N+3 + * ------------------------------------------------ + * Size | 1 | 1 | 1 | N | + * Data | 1 | race id | N (kart name size) | kart name | + * ------------------------------------------------ + */ +void ClientLobbyRoomProtocol::kartSelectionUpdate(Event* event) +{ + NetworkString data = event->data(); + if (data.size() < 3 || data[0] != 1) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a kart selection update wasn't formated as expected."); + return; + } + uint8_t player_id = data[1]; + uint8_t kart_name_length = data[2]; + std::string kart_name = data.getString(3, kart_name_length); + if (kart_name.size() != kart_name_length) + { + Log::error("ClientLobbyRoomProtocol", "Kart names sizes differ: told: %d, real: %d.", kart_name_length, kart_name.size()); + return; + } + if (!m_setup->isKartAvailable(kart_name)) + { + Log::error("ClientLobbyRoomProtocol", "The updated kart is taken already."); + } + m_setup->setPlayerKart(player_id, kart_name); + NetworkKartSelectionScreen::getInstance()->playerSelected(player_id, kart_name); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the race needs to be started. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 + * ------------- + * Size | 1 | 4 | + * Data | 4 | token | + * ------------- + */ +void ClientLobbyRoomProtocol::startGame(Event* event) +{ + NetworkString data = event->data(); + if (data.size() < 5 || data[0] != 4) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a kart " + "selection update wasn't formated as expected."); + return; + } + uint8_t token = data.gui32(1); + if (token == NetworkManager::getInstance()->getPeers()[0]->getClientServerToken()) + { + m_state = PLAYING; + m_listener->requestStart(new StartGameProtocol(m_setup)); + Log::error("ClientLobbyRoomProtocol", "Starting new game"); + } + else + Log::error("ClientLobbyRoomProtocol", "Bad token when starting game"); + +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the kart selection starts. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 + * ------------- + * Size | 1 | 4 | + * Data | 4 | token | + * ------------- + */ +void ClientLobbyRoomProtocol::startSelection(Event* event) +{ + NetworkString data = event->data(); + if (data.size() < 5 || data[0] != 4) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a kart " + "selection update wasn't formated as expected."); + return; + } + uint8_t token = data.gui32(1); + if (token == NetworkManager::getInstance()->getPeers()[0]->getClientServerToken()) + { + m_state = KART_SELECTION; + Log::info("ClientLobbyRoomProtocol", "Kart selection starts now"); + } + else + Log::error("ClientLobbyRoomProtocol", "Bad token"); + +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when all karts have finished the race. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 + * --------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | | + * Data | 4 | token | 1 | Kart 1 ID | 1 | kart id 2 | ... | + * --------------------------------------------------- + */ +void ClientLobbyRoomProtocol::raceFinished(Event* event) +{ + if (event->data().size() < 5) + { + Log::error("ClientLobbyRoomProtocol", "Not enough data provided."); + return; + } + NetworkString data = event->data(); + if ((*event->peer)->getClientServerToken() != data.gui32(1)) + { + Log::error("ClientLobbyRoomProtocol", "Bad token"); + return; + } + data.removeFront(5); + Log::error("ClientLobbyRoomProtocol", "Server notified that the race is finished."); + + // stop race protocols + Protocol* protocol = NULL; + protocol = m_listener->getProtocol(PROTOCOL_CONTROLLER_EVENTS); + if (protocol) + m_listener->requestTerminate(protocol); + else + Log::error("ClientLobbyRoomProtocol", "No controller events protocol registered."); + + protocol = m_listener->getProtocol(PROTOCOL_KART_UPDATE); + if (protocol) + m_listener->requestTerminate(protocol); + else + Log::error("ClientLobbyRoomProtocol", "No kart update protocol registered."); + + protocol = m_listener->getProtocol(PROTOCOL_GAME_EVENTS); + if (protocol) + m_listener->requestTerminate(protocol); + else + Log::error("ClientLobbyRoomProtocol", "No game events protocol registered."); + + // finish the race + WorldWithRank* ranked_world = (WorldWithRank*)(World::getWorld()); + ranked_world->beginSetKartPositions(); + ranked_world->setPhase(WorldStatus::RESULT_DISPLAY_PHASE); + int position = 1; + while(data.size()>0) + { + if (data.size() < 2) + { + Log::error("ClientLobbyRoomProtocol", "Incomplete field."); + return; + } + if (data[0] != 1) + { + Log::error("ClientLobbyRoomProtocol", "Badly formatted field."); + return; + } + uint8_t kart_id = data[1]; + ranked_world->setKartPosition(kart_id,position); + Log::info("ClientLobbyRoomProtocol", "Kart %d has finished #%d", kart_id, position); + data.removeFront(2); + position++; + } + ranked_world->endSetKartPositions(); + m_state = RACE_FINISHED; + ranked_world->terminateRace(); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a major race mode. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 + * -------------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | + * Data | 4 | priv token | 1 | player id | 1 | major mode vote | + * -------------------------------------------------------- + */ +void ClientLobbyRoomProtocol::playerMajorVote(Event* event) +{ + NetworkString data = event->data(); + if (!checkDataSizeAndToken(event, 9)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + if (!isByteCorrect(event, 7, 1)) + return; + m_setup->getRaceConfig()->setPlayerMajorVote(data[6], data[8]); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for the number of races in a GP. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 + * ---------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | + * Data | 4 | priv token | 1 | player id | 1 | races count | + * ---------------------------------------------------- + */ +void ClientLobbyRoomProtocol::playerRaceCountVote(Event* event) +{ + NetworkString data = event->data(); + if (!checkDataSizeAndToken(event, 9)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + if (!isByteCorrect(event, 7, 1)) + return; + m_setup->getRaceConfig()->setPlayerRaceCountVote(data[6], data[8]); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a minor race mode. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 + * -------------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | + * Data | 4 | priv token | 1 | player id | 1 | minor mode vote | + * -------------------------------------------------------- + */ +void ClientLobbyRoomProtocol::playerMinorVote(Event* event) +{ + NetworkString data = event->data(); + if (!checkDataSizeAndToken(event, 9)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + if (!isByteCorrect(event, 7, 1)) + return; + m_setup->getRaceConfig()->setPlayerMinorVote(data[6], data[8]); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a track. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 N+8 N+9 N+10 + * --------------------------------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | N | 1 | 1 | + * Data | 4 | priv token | 1 | player id | N | track name | 1 | track number (gp) | + * --------------------------------------------------------------------------- + */ +void ClientLobbyRoomProtocol::playerTrackVote(Event* event) +{ + NetworkString data = event->data(); + if (!checkDataSizeAndToken(event, 10)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + int N = data[7]; + std::string track_name = data.gs(8, N); + if (!isByteCorrect(event, N+8, 1)) + return; + m_setup->getRaceConfig()->setPlayerTrackVote(data[6], track_name, data[N+9]); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for the reverse mode of a race + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 10 11 + * ------------------------------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | 1 | 1 | + * Data | 4 | priv token | 1 | player id | 1 | reversed | 1 | track number (gp) | + * ------------------------------------------------------------------------- + */ +void ClientLobbyRoomProtocol::playerReversedVote(Event* event) +{ + NetworkString data = event->data(); + if (!checkDataSizeAndToken(event, 11)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + if (!isByteCorrect(event, 7, 1)) + return; + if (!isByteCorrect(event, 9, 1)) + return; + m_setup->getRaceConfig()->setPlayerReversedVote(data[6], data[8]!=0, data[10]); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a major race mode. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 10 11 + * --------------------------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | 1 | 1 | + * Data | 4 | priv token | 1 | player id | 1 | laps | 1 | track number (gp) | + * --------------------------------------------------------------------- + */ +void ClientLobbyRoomProtocol::playerLapsVote(Event* event) +{ + NetworkString data = event->data(); + if (!checkDataSizeAndToken(event, 9)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + if (!isByteCorrect(event, 7, 1)) + return; + m_setup->getRaceConfig()->setPlayerLapsVote(data[6], data[8], data[10]); +} +//----------------------------------------------------------------------------- diff --git a/src/network/protocols/client_lobby_room_protocol.hpp b/src/network/protocols/client_lobby_room_protocol.hpp new file mode 100644 index 000000000..d57fd067e --- /dev/null +++ b/src/network/protocols/client_lobby_room_protocol.hpp @@ -0,0 +1,65 @@ +#ifndef CLIENT_LOBBY_ROOM_PROTOCOL_HPP +#define CLIENT_LOBBY_ROOM_PROTOCOL_HPP + +#include "network/protocols/lobby_room_protocol.hpp" + +class ClientLobbyRoomProtocol : public LobbyRoomProtocol +{ + public: + ClientLobbyRoomProtocol(const TransportAddress& server_address); + virtual ~ClientLobbyRoomProtocol(); + + void requestKartSelection(std::string kart_name); + void voteMajor(uint8_t major); + void voteRaceCount(uint8_t count); + void voteMinor(uint8_t minor); + void voteTrack(std::string track, uint8_t track_nb = 0); + void voteReversed(bool reversed, uint8_t track_nb = 0); + void voteLaps(uint8_t laps, uint8_t track_nb = 0); + void sendMessage(std::string message); + void leave(); + + virtual bool notifyEvent(Event* event); + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {} + + protected: + void newPlayer(Event* event); + void disconnectedPlayer(Event* event); + void connectionAccepted(Event* event); //!< Callback function on connection acceptation + void connectionRefused(Event* event); //!< Callback function on connection refusal + void kartSelectionRefused(Event* event); + void kartSelectionUpdate(Event* event); + void startGame(Event* event); + void startSelection(Event* event); + void raceFinished(Event* event); + // race votes + void playerMajorVote(Event* event); + void playerRaceCountVote(Event* event); + void playerMinorVote(Event* event); + void playerTrackVote(Event* event); + void playerReversedVote(Event* event); + void playerLapsVote(Event* event); + + TransportAddress m_server_address; + STKPeer* m_server; + + enum STATE + { + NONE, + LINKED, + REQUESTING_CONNECTION, + CONNECTED, // means in the lobby room + KART_SELECTION, + SELECTING_KARTS, // in the network kart selection screen + PLAYING, + RACE_FINISHED, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // CLIENT_LOBBY_ROOM_PROTOCOL_HPP diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp new file mode 100644 index 000000000..907999c9e --- /dev/null +++ b/src/network/protocols/connect_to_peer.cpp @@ -0,0 +1,139 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/connect_to_peer.hpp" + +#include "network/client_network_manager.hpp" +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/request_connection.hpp" +#include "network/protocols/ping_protocol.hpp" +#include "online/current_user.hpp" +#include "utils/time.hpp" +#include "utils/log.hpp" + +// ---------------------------------------------------------------------------- + +ConnectToPeer::ConnectToPeer(uint32_t peer_id) : + Protocol(NULL, PROTOCOL_CONNECTION) +{ + m_peer_id = peer_id; + m_state = NONE; +} + +// ---------------------------------------------------------------------------- + +ConnectToPeer::~ConnectToPeer() +{ +} + +// ---------------------------------------------------------------------------- + +bool ConnectToPeer::notifyEventAsynchronous(Event* event) +{ + if (event->type == EVENT_TYPE_CONNECTED) + { + Log::debug("ConnectToPeer", "Received event notifying peer connection."); + m_state = CONNECTED; // we received a message, we are connected + } + return true; +} + +// ---------------------------------------------------------------------------- + +void ConnectToPeer::setup() +{ + m_state = NONE; + m_public_address.ip = 0; + m_public_address.port = 0; + m_peer_address.ip = 0; + m_peer_address.port = 0; + m_current_protocol_id = 0; +} + +// ---------------------------------------------------------------------------- + +void ConnectToPeer::asynchronousUpdate() +{ + switch(m_state) + { + case NONE: + { + m_current_protocol_id = m_listener->requestStart(new GetPeerAddress(m_peer_id, &m_peer_address)); + m_state = WAITING_PEER_ADDRESS; + break; + } + case WAITING_PEER_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // we know the peer address + { + if (m_peer_address.ip != 0 && m_peer_address.port != 0) + { + // we're in the same lan (same public ip address) !! + if (m_peer_address.ip == NetworkManager::getInstance()->getPublicAddress().ip) + { + // just send a broadcast packet with the string aloha_stk inside, the client will know our ip address and will connect + STKHost* host = NetworkManager::getInstance()->getHost(); + TransportAddress broadcast_address; + broadcast_address.ip = -1; // 255.255.255.255 + broadcast_address.port = m_peer_address.port; // 0b10101100000101101101111111111111; // for test + char data[] = "aloha_stk\0"; + host->sendRawPacket((uint8_t*)(data), 10, broadcast_address); + Log::info("ConnectToPeer", "Broadcast aloha sent."); + StkTime::sleep(1); + broadcast_address.ip = 0x7f000001; // 127.0.0.1 (localhost) + broadcast_address.port = m_peer_address.port; + host->sendRawPacket((uint8_t*)(data), 10, broadcast_address); + Log::info("ConnectToPeer", "Broadcast aloha to self."); + } + else + { + m_current_protocol_id = m_listener->requestStart(new PingProtocol(m_peer_address, 2.0)); + } + m_state = CONNECTING; + } + else + { + Log::error("ConnectToPeer", "The peer you want to connect to has hidden his address."); + m_state = DONE; + } + } + break; + case CONNECTING: // waiting the peer to connect + break; + case CONNECTED: + { + // the ping protocol is there for NAT traversal (disabled when connecting to LAN peer) + if (m_peer_address != NetworkManager::getInstance()->getPublicAddress()) + m_listener->requestTerminate( m_listener->getProtocol(m_current_protocol_id)); // kill the ping protocol because we're connected + m_state = DONE; + break; + } + case DONE: + m_state = EXITING; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + +// ---------------------------------------------------------------------------- + diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp new file mode 100644 index 000000000..97bad8327 --- /dev/null +++ b/src/network/protocols/connect_to_peer.hpp @@ -0,0 +1,55 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 CONNECT_TO_SERVER_HPP +#define CONNECT_TO_SERVER_HPP + +#include "network/protocol.hpp" +#include "network/types.hpp" +#include + +class ConnectToPeer : public Protocol, public CallbackObject +{ + public: + ConnectToPeer(uint32_t peer_id); + virtual ~ConnectToPeer(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + TransportAddress m_peer_address; + TransportAddress m_public_address; + uint32_t m_peer_id; + uint32_t m_current_protocol_id; + + enum STATE + { + NONE, + WAITING_PEER_ADDRESS, + CONNECTING, + CONNECTED, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp new file mode 100644 index 000000000..c9b70ae7c --- /dev/null +++ b/src/network/protocols/connect_to_server.cpp @@ -0,0 +1,283 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/connect_to_server.hpp" + +#include "network/client_network_manager.hpp" +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/request_connection.hpp" +#include "network/protocols/ping_protocol.hpp" +#include "network/protocols/quick_join_protocol.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" +#include "online/current_user.hpp" +#include "utils/time.hpp" +#include "utils/log.hpp" + +#ifdef WIN32 +# include +#else +#include +#endif + +// ---------------------------------------------------------------------------- + +ConnectToServer::ConnectToServer() : + Protocol(NULL, PROTOCOL_CONNECTION) +{ + m_server_id = 0; + m_quick_join = true; + m_state = NONE; +} + +// ---------------------------------------------------------------------------- + +ConnectToServer::ConnectToServer(uint32_t server_id, uint32_t host_id) : + Protocol(NULL, PROTOCOL_CONNECTION) +{ + m_server_id = server_id; + m_host_id = host_id; + m_quick_join = false; + m_state = NONE; +} + +// ---------------------------------------------------------------------------- + +ConnectToServer::~ConnectToServer() +{ +} + +// ---------------------------------------------------------------------------- + +bool ConnectToServer::notifyEventAsynchronous(Event* event) +{ + if (event->type == EVENT_TYPE_CONNECTED) + { + Log::info("ConnectToServer", "The Connect To Server protocol has " + "received an event notifying that he's connected to the peer."); + m_state = CONNECTED; // we received a message, we are connected + } + return true; +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::setup() +{ + Log::info("ConnectToServer", "SETUPP"); + m_state = NONE; + m_public_address.ip = 0; + m_public_address.port = 0; + m_server_address.ip = 0; + m_server_address.port = 0; + m_current_protocol_id = 0; +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::asynchronousUpdate() +{ + switch(m_state) + { + case NONE: + { + Log::info("ConnectToServer", "Protocol starting"); + m_current_protocol_id = m_listener->requestStart(new GetPublicAddress(&m_public_address)); + m_state = GETTING_SELF_ADDRESS; + break; + } + case GETTING_SELF_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // now we know the public addr + { + m_state = SHOWING_SELF_ADDRESS; + NetworkManager::getInstance()->setPublicAddress(m_public_address); // set our public address + m_current_protocol_id = m_listener->requestStart(new ShowPublicAddress()); + Log::info("ConnectToServer", "Public address known"); + /* + if (m_quick_join) + m_current_protocol_id = m_listener->requestStart(new QuickJoinProtocol(&m_server_address, &m_server_id)); + else + m_current_protocol_id = m_listener->requestStart(new GetPeerAddress(m_server_id, &m_server_address));*/ + } + break; + case SHOWING_SELF_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // now our public address is in the database + { + Log::info("ConnectToServer", "Public address shown"); + if (m_quick_join) + { + m_current_protocol_id = m_listener->requestStart(new QuickJoinProtocol(&m_server_address, &m_server_id)); + m_state = REQUESTING_CONNECTION; + } + else + { + m_current_protocol_id = m_listener->requestStart(new GetPeerAddress(m_host_id, &m_server_address)); + m_state = GETTING_SERVER_ADDRESS; + } + } + break; + case GETTING_SERVER_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // we know the server address + { + Log::info("ConnectToServer", "Server's address known"); + if (m_server_address.ip == m_public_address.ip) // we're in the same lan (same public ip address) !! + Log::info("ConnectToServer", "Server appears to be in the same LAN."); + m_state = REQUESTING_CONNECTION; + m_current_protocol_id = m_listener->requestStart(new RequestConnection(m_server_id)); + } + break; + case REQUESTING_CONNECTION: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // server knows we wanna connect + { + Log::info("ConnectToServer", "Connection request made"); + if (m_server_address.ip == 0 || m_server_address.port == 0) + { // server data not correct, hide address and stop + m_state = HIDING_ADDRESS; + Log::error("ConnectToServer", "Server address is "ADDRESS_FORMAT, ADDRESS_ARGS(m_server_address.ip, m_server_address.port)); + m_current_protocol_id = m_listener->requestStart(new HidePublicAddress()); + return; + } + if (m_server_address.ip == m_public_address.ip) // we're in the same lan (same public ip address) !! + { + // just send a broadcast packet, the client will know our ip address and will connect + STKHost* host = NetworkManager::getInstance()->getHost(); + host->stopListening(); // stop the listening + TransportAddress sender; + + TransportAddress broadcast_address; + broadcast_address.ip = -1; // 255.255.255.255 + broadcast_address.port = 7321; // 0b10101100000101101101111111111111; // for test + char data2[] = "aloha_stk\0"; + host->sendRawPacket((uint8_t*)(data2), 10, broadcast_address); + + Log::info("ConnectToServer", "Waiting broadcast message."); + const uint8_t* received_data = host->receiveRawPacket(&sender); // get the sender + + host->startListening(); // start listening again + const char data[] = "aloha_stk\0"; + if (strcmp(data, (char*)(received_data)) == 0) + { + Log::info("ConnectToServer", "LAN Server found : %u:%u", sender.ip, sender.port); +#ifndef WIN32 + // just check if the ip is ours : if so, then just use localhost (127.0.0.1) + struct ifaddrs *ifap, *ifa; + struct sockaddr_in *sa; + getifaddrs (&ifap); // get the info + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr->sa_family==AF_INET) + { + sa = (struct sockaddr_in *) ifa->ifa_addr; + if (ntohl(sa->sin_addr.s_addr) == sender.ip) // this interface is ours + sender.ip = 0x7f000001; // 127.0.0.1 + } + } + freeifaddrs(ifap); +#else + // Query the list of all IP addresses on the local host + // First call to GetIpAddrTable with 0 bytes buffer + // will return insufficient buffer error, and size + // will contain the number of bytes needed for all + // data. Repeat the process of querying the size + // using GetIpAddrTable in a while loop since it + // can happen that an interface comes online between + // the previous call to GetIpAddrTable and the next + // call. + MIB_IPADDRTABLE *table = NULL; + unsigned long size = 0; + int error = GetIpAddrTable(table, &size, 0); + // Also add a count to limit the while loop - in + // case that something strange is going on. + int count = 0; + while(error==ERROR_INSUFFICIENT_BUFFER && count < 10) + { + delete table; // deleting NULL is legal + table = (MIB_IPADDRTABLE*)new char[size]; + error = GetIpAddrTable(table, &size, 0); + count ++; + } // while insufficient buffer + for(unsigned int i=0; idwNumEntries; i++) + { + int ip = ntohl(table->table[i].dwAddr); + if(sender.ip == ip) // this interface is ours + { + sender.ip = 0x7f000001; // 127.0.0.1 + break; + } + } + delete table; + +#endif + m_server_address = sender; + m_state = CONNECTING; + } + } + else + { + m_state = CONNECTING; + m_current_protocol_id = m_listener->requestStart(new PingProtocol(m_server_address, 2.0)); + } + } + break; + case CONNECTING: // waiting the server to answer our connection + { + static double timer = 0; + if (StkTime::getRealTime() > timer+5.0) // every 5 seconds + { + timer = StkTime::getRealTime(); + NetworkManager::getInstance()->connect(m_server_address); + Log::info("ConnectToServer", "Trying to connect to %u:%u", m_server_address.ip, m_server_address.port); + } + break; + } + case CONNECTED: + { + Log::info("ConnectToServer", "Connected"); + m_listener->requestTerminate( m_listener->getProtocol(m_current_protocol_id)); // kill the ping protocol because we're connected + m_current_protocol_id = m_listener->requestStart(new HidePublicAddress()); + ClientNetworkManager::getInstance()->setConnected(true); + m_state = HIDING_ADDRESS; + break; + } + case HIDING_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // we have hidden our address + { + Log::info("ConnectToServer", "Address hidden"); + m_state = DONE; + if (ClientNetworkManager::getInstance()->isConnected()) // lobby room protocol if we're connected only + m_listener->requestStart(new ClientLobbyRoomProtocol(m_server_address)); + } + break; + case DONE: + m_listener->requestTerminate(this); + m_state = EXITING; + break; + case EXITING: + break; + } +} + +// ---------------------------------------------------------------------------- + diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp new file mode 100644 index 000000000..7f95a512a --- /dev/null +++ b/src/network/protocols/connect_to_server.hpp @@ -0,0 +1,62 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 CONNECT_TO_SERVER_HPP +#define CONNECT_TO_SERVER_HPP + +#include "network/protocol.hpp" +#include "network/types.hpp" +#include + +class ConnectToServer : public Protocol, public CallbackObject +{ + public: + ConnectToServer(); //!< Quick join + ConnectToServer(uint32_t server_id, uint32_t host_id); //!< Specify server id + virtual ~ConnectToServer(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + TransportAddress m_server_address; + TransportAddress m_public_address; + uint32_t m_server_id; + uint32_t m_host_id; + uint32_t m_current_protocol_id; + bool m_quick_join; + + enum STATE + { + NONE, + GETTING_SELF_ADDRESS, + SHOWING_SELF_ADDRESS, + GETTING_SERVER_ADDRESS, + REQUESTING_CONNECTION, + CONNECTING, + CONNECTED, + HIDING_ADDRESS, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/controller_events_protocol.cpp b/src/network/protocols/controller_events_protocol.cpp new file mode 100644 index 000000000..8a6067a68 --- /dev/null +++ b/src/network/protocols/controller_events_protocol.cpp @@ -0,0 +1,171 @@ +#include "network/protocols/controller_events_protocol.hpp" + +#include "modes/world.hpp" +#include "karts/abstract_kart.hpp" +#include "network/network_manager.hpp" +#include "network/network_world.hpp" +#include "utils/log.hpp" + +//----------------------------------------------------------------------------- + +ControllerEventsProtocol::ControllerEventsProtocol() : + Protocol(NULL, PROTOCOL_CONTROLLER_EVENTS) +{ +} + +//----------------------------------------------------------------------------- + +ControllerEventsProtocol::~ControllerEventsProtocol() +{ +} + +//----------------------------------------------------------------------------- + +void ControllerEventsProtocol::setup() +{ + m_self_controller_index = 0; + std::vector karts = World::getWorld()->getKarts(); + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < karts.size(); i++) + { + if (karts[i]->getIdent() == NetworkWorld::getInstance()->m_self_kart) + { + Log::info("ControllerEventsProtocol", "My id is %d", i); + m_self_controller_index = i; + } + STKPeer* peer = NULL; + if (m_listener->isServer()) + { + for (unsigned int j = 0; j < peers.size(); j++) + { + if (peers[j]->getPlayerProfile()->kart_name == karts[i]->getIdent()) + { + peer = peers[j]; + } + Log::info("ControllerEventsProtocol", "Compared %s and %s", + peers[j]->getPlayerProfile()->kart_name.c_str(), karts[i]->getIdent().c_str()); + } + } + else + { + if (peers.size() > 0) + peer = peers[0]; + } + if (peer == NULL) + { + Log::error("ControllerEventsProtocol", "Couldn't find the peer corresponding to the kart."); + } + m_controllers.push_back(std::pair(karts[i]->getController(), peer)); + } +} + +//----------------------------------------------------------------------------- + +bool ControllerEventsProtocol::notifyEventAsynchronous(Event* event) +{ + NetworkString data = event->data(); + if (data.size() < 17) + { + Log::error("ControllerEventsProtocol", "The data supplied was not complete. Size was %d.", data.size()); + return true; + } + uint32_t token = data.gui32(); + NetworkString pure_message = data; + pure_message.removeFront(4); + if (token != (*event->peer)->getClientServerToken()) + { + Log::error("ControllerEventsProtocol", "Bad token from peer."); + return true; + } + NetworkString ns = pure_message; + + ns.removeFront(4); + uint8_t client_index = -1; + while (ns.size() >= 9) + { + uint8_t controller_index = ns.gui8(); + client_index = controller_index; + uint8_t serialized_1 = ns.gui8(1); + PlayerAction action = (PlayerAction)(ns.gui8(4)); + int action_value = ns.gui32(5); + + KartControl* controls = m_controllers[controller_index].first->getControls(); + controls->m_brake = (serialized_1 & 0x40)!=0; + controls->m_nitro = (serialized_1 & 0x20)!=0; + controls->m_rescue = (serialized_1 & 0x10)!=0; + controls->m_fire = (serialized_1 & 0x08)!=0; + controls->m_look_back = (serialized_1 & 0x04)!=0; + controls->m_skid = KartControl::SkidControl(serialized_1 & 0x03); + + m_controllers[controller_index].first->action(action, action_value); + ns.removeFront(9); + //Log::info("ControllerEventProtocol", "Registered one action."); + } + if (ns.size() > 0 && ns.size() != 9) + { + Log::warn("ControllerEventProtocol", "The data seems corrupted. Remains %d", ns.size()); + return true; + } + if (client_index < 0) + { + Log::warn("ControllerEventProtocol", "Couldn't have a client id."); + return true; + } + if (m_listener->isServer()) + { + // notify everybody of the event : + for (unsigned int i = 0; i < m_controllers.size(); i++) + { + if (i == client_index) // don't send that message to the sender + continue; + NetworkString ns2; + ns2.ai32(m_controllers[i].second->getClientServerToken()); + ns2 += pure_message; + m_listener->sendMessage(this, m_controllers[i].second, ns2, false); + //Log::info("ControllerEventsProtocol", "Sizes are %d and %d", ns2.size(), pure_message.size()); + } + } + return true; +} + +//----------------------------------------------------------------------------- + +void ControllerEventsProtocol::update() +{ +} + +//----------------------------------------------------------------------------- + +void ControllerEventsProtocol::controllerAction(Controller* controller, + PlayerAction action, int value) +{ + assert(!m_listener->isServer()); + + KartControl* controls = controller->getControls(); + uint8_t serialized_1 = 0; + serialized_1 |= (controls->m_brake==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_nitro==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_rescue==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_fire==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_look_back==true); + serialized_1 <<= 2; + serialized_1 += controls->m_skid; + uint8_t serialized_2 = (uint8_t)(controls->m_accel*255.0); + uint8_t serialized_3 = (uint8_t)(controls->m_steer*127.0); + + NetworkString ns; + ns.ai32(m_controllers[m_self_controller_index].second->getClientServerToken()); + ns.af(World::getWorld()->getTime()); + ns.ai8(m_self_controller_index); + ns.ai8(serialized_1).ai8(serialized_2).ai8(serialized_3); + ns.ai8((uint8_t)(action)).ai32(value); + + Log::info("ControllerEventsProtocol", "Action %d value %d", action, value); + m_listener->sendMessage(this, ns, false); // send message to server +} + + diff --git a/src/network/protocols/controller_events_protocol.hpp b/src/network/protocols/controller_events_protocol.hpp new file mode 100644 index 000000000..9a65c0d2c --- /dev/null +++ b/src/network/protocols/controller_events_protocol.hpp @@ -0,0 +1,28 @@ +#ifndef CONTROLLER_EVENTS_PROTOCOL_HPP +#define CONTROLLER_EVENTS_PROTOCOL_HPP + +#include "network/protocol.hpp" + +#include "input/input.hpp" +#include "karts/controller/controller.hpp" + +class ControllerEventsProtocol : public Protocol +{ + protected: + std::vector > m_controllers; + uint32_t m_self_controller_index; + + public: + ControllerEventsProtocol(); + virtual ~ControllerEventsProtocol(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {} + + void controllerAction(Controller* controller, PlayerAction action, int value); + +}; + +#endif // CONTROLLER_EVENTS_PROTOCOL_HPP diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp new file mode 100644 index 000000000..fea90ab89 --- /dev/null +++ b/src/network/protocols/game_events_protocol.cpp @@ -0,0 +1,94 @@ +#include "network/protocols/game_events_protocol.hpp" + +#include "karts/abstract_kart.hpp" +#include "items/attachment.hpp" +#include "items/item.hpp" +#include "items/item_manager.hpp" +#include "items/powerup.hpp" +#include "modes/world.hpp" +#include "network/network_manager.hpp" + +GameEventsProtocol::GameEventsProtocol() : Protocol(NULL, PROTOCOL_GAME_EVENTS) +{ +} + +GameEventsProtocol::~GameEventsProtocol() +{ +} + +bool GameEventsProtocol::notifyEvent(Event* event) +{ + if (event->type != EVENT_TYPE_MESSAGE) + return true; + NetworkString data = event->data(); + if (data.size() < 5) // for token and type + { + Log::warn("GameEventsProtocol", "Too short message."); + return true; + } + if ( (*event->peer)->getClientServerToken() != data.gui32()) + { + Log::warn("GameEventsProtocol", "Bad token."); + return true; + } + int8_t type = data.gui8(4); + data.removeFront(5); + switch (type) + { + case 0x01: // item picked + { + if (data.size() < 6) + { + Log::warn("GameEventsProtocol", "Too short message."); + return true; + } + uint32_t item_id = data.gui32(); + uint8_t powerup_type = data.gui8(4); + uint8_t kart_race_id = data.gui8(5); + // now set the kart powerup + AbstractKart* kart = World::getWorld()->getKart( + NetworkManager::getInstance()->getGameSetup()->getProfile(kart_race_id)->world_kart_id); + ItemManager::get()->collectedItem( + ItemManager::get()->getItem(item_id), + kart, + powerup_type); + Log::info("GameEventsProtocol", "Item %d picked by a player.", powerup_type); + } break; + default: + Log::warn("GameEventsProtocol", "Unkown message type."); + break; + } + return true; +} + +void GameEventsProtocol::setup() +{ +} + +void GameEventsProtocol::update() +{ +} + +void GameEventsProtocol::collectedItem(Item* item, AbstractKart* kart) +{ + GameSetup* setup = NetworkManager::getInstance()->getGameSetup(); + assert(setup); + const NetworkPlayerProfile* player_profile = setup->getProfile(kart->getIdent()); // use kart name + + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai32(peers[i]->getClientServerToken()); + // 0x01 : item picked : send item id, powerup type and kart race id + uint8_t powerup = 0; + if (item->getType() == Item::ITEM_BANANA) + powerup = (int)(kart->getAttachment()->getType()); + else if (item->getType() == Item::ITEM_BONUS_BOX) + powerup = (((int)(kart->getPowerup()->getType()) << 4)&0xf0) + (kart->getPowerup()->getNum()&0x0f); + + ns.ai8(0x01).ai32(item->getItemId()).ai8(powerup).ai8(player_profile->race_id); // send item, + m_listener->sendMessage(this, peers[i], ns, true); // reliable + Log::info("GameEventsProtocol", "Notified a peer that a kart collected item %d.", (int)(kart->getPowerup()->getType())); + } +} diff --git a/src/network/protocols/game_events_protocol.hpp b/src/network/protocols/game_events_protocol.hpp new file mode 100644 index 000000000..9d85b0104 --- /dev/null +++ b/src/network/protocols/game_events_protocol.hpp @@ -0,0 +1,26 @@ +#ifndef GAME_EVENTS_PROTOCOL_HPP +#define GAME_EVENTS_PROTOCOL_HPP + +#include "network/protocol.hpp" + +class AbstractKart; +class Item; + +class GameEventsProtocol : public Protocol +{ + public: + GameEventsProtocol(); + virtual ~GameEventsProtocol(); + + virtual bool notifyEvent(Event* event); + virtual bool notifyEventAsynchronous(Event* event) { return false; } + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {} + + void collectedItem(Item* item, AbstractKart* kart); + + protected: +}; + +#endif // GAME_EVENTS_PROTOCOL_HPP diff --git a/src/network/protocols/get_peer_address.cpp b/src/network/protocols/get_peer_address.cpp new file mode 100644 index 000000000..0c5caae8b --- /dev/null +++ b/src/network/protocols/get_peer_address.cpp @@ -0,0 +1,97 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/get_peer_address.hpp" + +#include "network/protocol_manager.hpp" +#include "network/network_manager.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +GetPeerAddress::GetPeerAddress(uint32_t peer_id, CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ + m_peer_id = peer_id; +} + +GetPeerAddress::~GetPeerAddress() +{ +} + +void GetPeerAddress::setup() +{ + m_state = NONE; + m_request = NULL; +} + +void GetPeerAddress::asynchronousUpdate() +{ + if (m_state == NONE) + { + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("peer_id",m_peer_id); + m_request->setParameter("action","get"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if (rec_success == "yes") + { + TransportAddress* addr = static_cast(m_callback_object); + result->get("ip", &addr->ip); + if (addr->ip == NetworkManager::getInstance()->getPublicAddress().ip) + result->get("private_port", &addr->port); + else + result->get("port", &addr->port); + Log::debug("GetPeerAddress", "Address gotten successfully."); + } + else + { + Log::error("GetPeerAddress", "Fail to get address."); + } + } + else + { + Log::error("GetPeerAddress", "Fail to get address."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} + +void GetPeerAddress::setPeerID(uint32_t peer_id) +{ + m_peer_id = peer_id; +} diff --git a/src/network/protocols/get_peer_address.hpp b/src/network/protocols/get_peer_address.hpp new file mode 100644 index 000000000..771825d97 --- /dev/null +++ b/src/network/protocols/get_peer_address.hpp @@ -0,0 +1,52 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 GET_PEER_ADDRESS_HPP +#define GET_PEER_ADDRESS_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +class GetPeerAddress : public Protocol +{ + public: + GetPeerAddress(uint32_t peer_id, CallbackObject* callback_object); + virtual ~GetPeerAddress(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + void setPeerID(uint32_t m_peer_id); + protected: + uint32_t m_peer_id; + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; + +}; + +#endif // GET_PEER_ADDRESS_HPP diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp new file mode 100644 index 000000000..6a3ed5d01 --- /dev/null +++ b/src/network/protocols/get_public_address.cpp @@ -0,0 +1,246 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/get_public_address.hpp" + +#include "config/user_config.hpp" +#include "network/network_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/network_interface.hpp" + +#include "utils/log.hpp" +#include "utils/random_generator.hpp" + +#include +#ifdef WIN32 +# include +# include +#else +# include +#endif +#include + +int stunRand() +{ + static bool init = false; + if (!init) + { + srand((unsigned int)time(NULL)); + init = true; + } + return rand(); +} + +GetPublicAddress::GetPublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ +} + +GetPublicAddress::~GetPublicAddress() +{ +} + +void GetPublicAddress::setup() +{ + m_state = NOTHING_DONE; +} + +void GetPublicAddress::asynchronousUpdate() +{ + if (m_state == NOTHING_DONE) + { + // format : 00MMMMMCMMMCMMMM (cf rfc 5389) + uint16_t message_type = 0x0001; // binding request + m_stun_tansaction_id[0] = stunRand(); + m_stun_tansaction_id[1] = stunRand(); + m_stun_tansaction_id[2] = stunRand(); + uint16_t message_length = 0x0000; + + uint8_t bytes[21]; // the message to be sent + // bytes 0-1 : the type of the message, + bytes[0] = (uint8_t)(message_type>>8); + bytes[1] = (uint8_t)(message_type); + + // bytes 2-3 : message length added to header (attributes) + bytes[2] = (uint8_t)(message_length>>8); + bytes[3] = (uint8_t)(message_length); + + // bytes 4-7 : magic cookie to recognize the stun protocol + bytes[4] = (uint8_t)(m_stun_magic_cookie>>24); + bytes[5] = (uint8_t)(m_stun_magic_cookie>>16); + bytes[6] = (uint8_t)(m_stun_magic_cookie>>8); + bytes[7] = (uint8_t)(m_stun_magic_cookie); + + // bytes 8-19 : the transaction id + bytes[8] = (uint8_t)(m_stun_tansaction_id[0]>>24); + bytes[9] = (uint8_t)(m_stun_tansaction_id[0]>>16); + bytes[10] = (uint8_t)(m_stun_tansaction_id[0]>>8); + bytes[11] = (uint8_t)(m_stun_tansaction_id[0]); + bytes[12] = (uint8_t)(m_stun_tansaction_id[1]>>24); + bytes[13] = (uint8_t)(m_stun_tansaction_id[1]>>16); + bytes[14] = (uint8_t)(m_stun_tansaction_id[1]>>8); + bytes[15] = (uint8_t)(m_stun_tansaction_id[1]); + bytes[16] = (uint8_t)(m_stun_tansaction_id[2]>>24); + bytes[17] = (uint8_t)(m_stun_tansaction_id[2]>>16); + bytes[18] = (uint8_t)(m_stun_tansaction_id[2]>>8); + bytes[19] = (uint8_t)(m_stun_tansaction_id[2]); + bytes[20] = '\0'; + + // time to pick a random stun server + std::vector stun_servers = UserConfigParams::m_stun_servers; + + RandomGenerator random_gen; + int rand_result = random_gen.get(stun_servers.size()); + Log::verbose("GetPublicAddress", "Using STUN server %s", stun_servers[rand_result]); + + // resolve the name into an IP address + struct addrinfo hints, *res, *p; + int status; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version + hints.ai_socktype = SOCK_STREAM; + + if ((status = getaddrinfo(stun_servers[rand_result], NULL, &hints, &res)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); + return; + } + for(p = res;p != NULL; p = p->ai_next) + { + struct sockaddr_in* current_interface = (struct sockaddr_in*)(p->ai_addr); + + m_stun_server_ip = ntohl(current_interface->sin_addr.s_addr); + m_transaction_host = new STKHost(); + m_transaction_host->setupClient(1,1,0,0); + m_transaction_host->sendRawPacket(bytes, 20, TransportAddress(m_stun_server_ip, 3478)); + m_state = TEST_SENT; + + freeaddrinfo(res); // free the linked list + return; + } + freeaddrinfo(res); // free the linked list + + } + if (m_state == TEST_SENT) + { + uint8_t* data = m_transaction_host->receiveRawPacket(TransportAddress(m_stun_server_ip, 3478), 2000); + if (!data) + { + m_state = NOTHING_DONE; // will send the test again to an other server + return; + } + assert(data); + + // check that the stun response is a response, contains the magic cookie and the transaction ID + if ( data[0] == 0x01 && + data[1] == 0x01 && + data[4] == (uint8_t)(m_stun_magic_cookie>>24) && + data[5] == (uint8_t)(m_stun_magic_cookie>>16) && + data[6] == (uint8_t)(m_stun_magic_cookie>>8) && + data[7] == (uint8_t)(m_stun_magic_cookie) ) + { + if( + data[8] == (uint8_t)(m_stun_tansaction_id[0]>>24) && + data[9] == (uint8_t)(m_stun_tansaction_id[0]>>16) && + data[10] == (uint8_t)(m_stun_tansaction_id[0]>>8 ) && + data[11] == (uint8_t)(m_stun_tansaction_id[0] ) && + data[12] == (uint8_t)(m_stun_tansaction_id[1]>>24) && + data[13] == (uint8_t)(m_stun_tansaction_id[1]>>16) && + data[14] == (uint8_t)(m_stun_tansaction_id[1]>>8 ) && + data[15] == (uint8_t)(m_stun_tansaction_id[1] ) && + data[16] == (uint8_t)(m_stun_tansaction_id[2]>>24) && + data[17] == (uint8_t)(m_stun_tansaction_id[2]>>16) && + data[18] == (uint8_t)(m_stun_tansaction_id[2]>>8 ) && + data[19] == (uint8_t)(m_stun_tansaction_id[2] )) + { + Log::verbose("GetPublicAddress", "The STUN server responded with a valid answer"); + int message_size = data[2]*256+data[3]; + + // parse the stun message now: + bool finish = false; + uint8_t* attributes = data+20; + if (message_size == 0) + { + Log::error("GetPublicAddress", "STUN answer does not contain any information."); + finish = true; + } + if (message_size < 4) // cannot even read the size + { + Log::error("GetPublicAddress", "STUN message is not valid."); + finish = true; + } + uint16_t port; + uint32_t address; + bool valid = false; + while(!finish) + { + int type = attributes[0]*256+attributes[1]; + int size = attributes[2]*256+attributes[3]; + switch(type) + { + case 0: + case 1: + assert(size == 8); + assert(attributes[5] = 0x01); // IPv4 only + port = attributes[6]*256+attributes[7]; + address = (attributes[8]<<24 & 0xFF000000)+(attributes[9]<<16 & 0x00FF0000)+(attributes[10]<<8 & 0x0000FF00)+(attributes[11] & 0x000000FF); + finish = true; + valid = true; + continue; + break; + default: + break; + } + attributes = attributes + 4 + size; + message_size -= 4 + size; + if (message_size == 0) + finish = true; + if (message_size < 4) // cannot even read the size + { + Log::error("GetPublicAddress", "STUN message is not valid."); + finish = true; + } + } + // finished parsing, we know our public transport address + if (valid) + { + Log::debug("GetPublicAddress", "The public address has been found : %i.%i.%i.%i:%i", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); + m_state = ADDRESS_KNOWN; + TransportAddress* addr = static_cast(m_callback_object); + addr->ip = address; + addr->port = port; + } + else + m_state = NOTHING_DONE; // need to re-send the stun request + } + else + { + m_state = NOTHING_DONE; // need to re-send the stun request + } + } + } + if (m_state == ADDRESS_KNOWN) + { + m_state = EXITING; + // terminate the protocol + m_listener->requestTerminate(this); + } + if (m_state == EXITING) + { + } +} diff --git a/src/network/protocols/get_public_address.hpp b/src/network/protocols/get_public_address.hpp new file mode 100644 index 000000000..408a031ba --- /dev/null +++ b/src/network/protocols/get_public_address.hpp @@ -0,0 +1,51 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 GET_PUBLIC_ADDRESS_HPP +#define GET_PUBLIC_ADDRESS_HPP + +#include "network/protocol.hpp" + +class GetPublicAddress : public Protocol +{ + public: + GetPublicAddress(CallbackObject* callback_object); + virtual ~GetPublicAddress(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + enum STATE + { + NOTHING_DONE, + TEST_SENT, + ADDRESS_KNOWN, + EXITING + }; + STATE m_state; + uint32_t m_stun_tansaction_id[3]; + static const uint32_t m_stun_magic_cookie = 0x2112A442; + uint32_t m_stun_server_ip; + STKHost* m_transaction_host; +}; + +#endif // GET_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/hide_public_address.cpp b/src/network/protocols/hide_public_address.cpp new file mode 100644 index 000000000..ec3b42a82 --- /dev/null +++ b/src/network/protocols/hide_public_address.cpp @@ -0,0 +1,82 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/hide_public_address.hpp" + +#include "network/protocol_manager.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +HidePublicAddress::HidePublicAddress() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +HidePublicAddress::~HidePublicAddress() +{ +} + +void HidePublicAddress::setup() +{ + m_state = NONE; +} + +void HidePublicAddress::asynchronousUpdate() +{ + if (m_state == NONE) + { + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("action","unset"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::debug("ShowPublicAddress", "Address hidden successfully."); + } + else + { + Log::error("ShowPublicAddress", "Fail to hide address."); + } + } + else + { + Log::error("ShowPublicAddress", "Fail to hide address."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/num_players_message.hpp b/src/network/protocols/hide_public_address.hpp similarity index 50% rename from src/network/num_players_message.hpp rename to src/network/protocols/hide_public_address.hpp index 4279f9eb9..e91ac3db7 100644 --- a/src/network/num_players_message.hpp +++ b/src/network/protocols/hide_public_address.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,27 +16,35 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NUM_PLAYERS_MESSAGE_HPP -#define HEADER_NUM_PLAYERS_MESSAGE_HPP +#ifndef HIDE_PUBLIC_ADDRESS_HPP +#define HIDE_PUBLIC_ADDRESS_HPP +#include "network/protocol.hpp" +#include "online/request.hpp" #include -#include -#ifndef WIN32 -# include -#endif -#include "network/message.hpp" -#include "race/race_manager.hpp" - -class NumPlayersMessage : public Message +class HidePublicAddress : public Protocol { -private: - int m_num_players -public: - NumPlayersMessage():Message(Message::MT_CONNECT) { m_num_players=race } - NumPlayersMessage(ENetPacket* pkt):Message(pkt) - { m_id=getString(); } - const std::string& - getNumPlayers() { return m_num_players; } -}; // ConnectMessage -#endif + public: + HidePublicAddress(); + virtual ~HidePublicAddress(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/kart_update_protocol.cpp b/src/network/protocols/kart_update_protocol.cpp new file mode 100644 index 000000000..67d351cd4 --- /dev/null +++ b/src/network/protocols/kart_update_protocol.cpp @@ -0,0 +1,137 @@ +#include "network/protocols/kart_update_protocol.hpp" + +#include "karts/abstract_kart.hpp" +#include "modes/world.hpp" +#include "network/protocol_manager.hpp" +#include "network/network_world.hpp" + +KartUpdateProtocol::KartUpdateProtocol() + : Protocol(NULL, PROTOCOL_KART_UPDATE) +{ + m_karts = World::getWorld()->getKarts(); + for (unsigned int i = 0; i < m_karts.size(); i++) + { + //if (m_karts[i]->getWorldKartId()) + { + Log::info("KartUpdateProtocol", "Kart %d has id %d and name %s", i, m_karts[i]->getWorldKartId(), m_karts[i]->getIdent().c_str()); + } + if (m_karts[i]->getIdent() == NetworkWorld::getInstance()->m_self_kart) + { + Log::info("KartUpdateProtocol", "My id is %d", i); + m_self_kart_index = i; + } + } + pthread_mutex_init(&m_positions_updates_mutex, NULL); +} + +KartUpdateProtocol::~KartUpdateProtocol() +{ +} + +bool KartUpdateProtocol::notifyEventAsynchronous(Event* event) +{ + if (event->type != EVENT_TYPE_MESSAGE) + return true; + NetworkString ns = event->data(); + if (ns.size() < 36) + { + Log::info("KartUpdateProtocol", "Message too short."); + return true; + } + ns.removeFront(4); + while(ns.size() >= 16) + { + uint32_t kart_id = ns.getUInt32(0); + + float a,b,c; + a = ns.getFloat(4); + b = ns.getFloat(8); + c = ns.getFloat(12); + float d,e,f,g; + d = ns.getFloat(16); + e = ns.getFloat(20); + f = ns.getFloat(24); + g = ns.getFloat(28); + pthread_mutex_trylock(&m_positions_updates_mutex); + m_next_positions.push_back(Vec3(a,b,c)); + m_next_quaternions.push_back(btQuaternion(d,e,f,g)); + m_karts_ids.push_back(kart_id); + pthread_mutex_unlock(&m_positions_updates_mutex); + ns.removeFront(32); + } + return true; +} + +void KartUpdateProtocol::setup() +{ +} + +void KartUpdateProtocol::update() +{ + if (!World::getWorld()) + return; + static double time = 0; + double current_time = StkTime::getRealTime(); + if (current_time > time + 0.1) // 10 updates per second + { + time = current_time; + if (m_listener->isServer()) + { + NetworkString ns; + ns.af( World::getWorld()->getTime()); + for (unsigned int i = 0; i < m_karts.size(); i++) + { + AbstractKart* kart = m_karts[i]; + Vec3 v = kart->getXYZ(); + btQuaternion quat = kart->getRotation(); + ns.ai32( kart->getWorldKartId()); + ns.af(v[0]).af(v[1]).af(v[2]); // add position + ns.af(quat.x()).af(quat.y()).af(quat.z()).af(quat.w()); // add rotation + Log::verbose("KartUpdateProtocol", "Sending %d's positions %f %f %f", kart->getWorldKartId(), v[0], v[1], v[2]); + } + m_listener->sendMessage(this, ns, false); + } + else + { + AbstractKart* kart = m_karts[m_self_kart_index]; + Vec3 v = kart->getXYZ(); + btQuaternion quat = kart->getRotation(); + NetworkString ns; + ns.af( World::getWorld()->getTime()); + ns.ai32( kart->getWorldKartId()); + ns.af(v[0]).af(v[1]).af(v[2]); // add position + ns.af(quat.x()).af(quat.y()).af(quat.z()).af(quat.w()); // add rotation + Log::verbose("KartUpdateProtocol", "Sending %d's positions %f %f %f", kart->getWorldKartId(), v[0], v[1], v[2]); + m_listener->sendMessage(this, ns, false); + } + } + switch(pthread_mutex_trylock(&m_positions_updates_mutex)) + { + case 0: /* if we got the lock */ + while (!m_next_positions.empty()) + { + uint32_t id = m_karts_ids.back(); + if (id != m_self_kart_index || m_listener->isServer()) // server takes all updates + { + Vec3 pos = m_next_positions.back(); + btTransform transform = m_karts[id]->getBody()->getInterpolationWorldTransform(); + transform.setOrigin(pos); + transform.setRotation(m_next_quaternions.back()); + m_karts[id]->getBody()->setCenterOfMassTransform(transform); + //m_karts[id]->getBody()->setLinearVelocity(Vec3(0,0,0)); + Log::verbose("KartUpdateProtocol", "Update kart %i pos to %f %f %f", id, pos[0], pos[1], pos[2]); + } + m_next_positions.pop_back(); + m_next_quaternions.pop_back(); + m_karts_ids.pop_back(); + } + pthread_mutex_unlock(&m_positions_updates_mutex); + break; + default: + break; + } +} + + + + diff --git a/src/network/protocols/kart_update_protocol.hpp b/src/network/protocols/kart_update_protocol.hpp new file mode 100644 index 000000000..fceb22616 --- /dev/null +++ b/src/network/protocols/kart_update_protocol.hpp @@ -0,0 +1,33 @@ +#ifndef KART_UPDATE_PROTOCOL_HPP +#define KART_UPDATE_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include "utils/vec3.hpp" +#include "LinearMath/btQuaternion.h" +#include + +class AbstractKart; + +class KartUpdateProtocol : public Protocol +{ + public: + KartUpdateProtocol(); + virtual ~KartUpdateProtocol(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {}; + + protected: + std::vector m_karts; + uint32_t m_self_kart_index; + + std::list m_next_positions; + std::list m_next_quaternions; + std::list m_karts_ids; + + pthread_mutex_t m_positions_updates_mutex; +}; + +#endif // KART_UPDATE_PROTOCOL_HPP diff --git a/src/network/world_loaded_message.hpp b/src/network/protocols/lobby_room_protocol.cpp similarity index 65% rename from src/network/world_loaded_message.hpp rename to src/network/protocols/lobby_room_protocol.cpp index 682e0fa05..46be6df03 100644 --- a/src/network/world_loaded_message.hpp +++ b/src/network/protocols/lobby_room_protocol.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,16 +16,16 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_WORLD_LOADED_HPP -#define HEADER_WORLD_LOADED_HPP +#include "network/protocols/lobby_room_protocol.hpp" -#include "network/message.hpp" - -class WorldLoadedMessage : public Message +LobbyRoomProtocol::LobbyRoomProtocol(CallbackObject* callback_object) : + Protocol(callback_object, PROTOCOL_LOBBY_ROOM) { -// For now this is an empty message -public: - WorldLoadedMessage() :Message(MT_WORLD_LOADED) {allocate(0);} - WorldLoadedMessage(ENetPacket* pkt):Message(pkt, MT_WORLD_LOADED) {} -}; // WorldLoadedMessage -#endif + m_setup = NULL; +} + +//----------------------------------------------------------------------------- + +LobbyRoomProtocol::~LobbyRoomProtocol() +{ +} diff --git a/src/network/protocols/lobby_room_protocol.hpp b/src/network/protocols/lobby_room_protocol.hpp new file mode 100644 index 000000000..d132359dd --- /dev/null +++ b/src/network/protocols/lobby_room_protocol.hpp @@ -0,0 +1,46 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 LOBBY_ROOM_PROTOCOL_HPP +#define LOBBY_ROOM_PROTOCOL_HPP + +#include "network/protocol.hpp" + +#include "network/game_setup.hpp" +#include "network/network_string.hpp" + +/*! + * \class LobbyRoomProtocol + * \brief Class used while the game is being prepared. + * This protocol starts when a server opens a game, or when a client joins a game. + * It is used to exchange data about the race settings, like kart selection. + */ +class LobbyRoomProtocol : public Protocol +{ + public: + LobbyRoomProtocol(CallbackObject* callback_object); + virtual ~LobbyRoomProtocol(); + + virtual void setup() = 0; + virtual void update() = 0; + + protected: + GameSetup* m_setup; //!< The game setup. +}; + +#endif // LOBBY_ROOM_PROTOCOL_HPP diff --git a/src/network/protocols/ping_protocol.cpp b/src/network/protocols/ping_protocol.cpp new file mode 100644 index 000000000..7fd5232ed --- /dev/null +++ b/src/network/protocols/ping_protocol.cpp @@ -0,0 +1,48 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/ping_protocol.hpp" + +#include "network/network_manager.hpp" +#include "utils/time.hpp" + +PingProtocol::PingProtocol(const TransportAddress& ping_dst, double delay_between_pings) : Protocol(NULL, PROTOCOL_SILENT) +{ + m_ping_dst = ping_dst; + m_delay_between_pings = delay_between_pings; +} + +PingProtocol::~PingProtocol() +{ +} + +void PingProtocol::setup() +{ + m_last_ping_time = 0; +} + +void PingProtocol::asynchronousUpdate() +{ + if (StkTime::getRealTime() > m_last_ping_time+m_delay_between_pings) + { + m_last_ping_time = StkTime::getRealTime(); + uint8_t data = 0; + NetworkManager::getInstance()->getHost()->sendRawPacket(&data, 1, m_ping_dst); + Log::info("PingProtocol", "Ping message sent"); + } +} diff --git a/src/network/protocols/ping_protocol.hpp b/src/network/protocols/ping_protocol.hpp new file mode 100644 index 000000000..70d2a7662 --- /dev/null +++ b/src/network/protocols/ping_protocol.hpp @@ -0,0 +1,25 @@ +#ifndef PING_PROTOCOL_HPP +#define PING_PROTOCOL_HPP + +#include "network/protocol.hpp" + + +class PingProtocol : public Protocol +{ + public: + PingProtocol(const TransportAddress& ping_dst, double delay_between_pings); + virtual ~PingProtocol(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + TransportAddress m_ping_dst; + double m_delay_between_pings; + double m_last_ping_time; +}; + +#endif // PING_PROTOCOL_HPP diff --git a/src/network/protocols/quick_join_protocol.cpp b/src/network/protocols/quick_join_protocol.cpp new file mode 100644 index 000000000..79d7b2eb7 --- /dev/null +++ b/src/network/protocols/quick_join_protocol.cpp @@ -0,0 +1,88 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "quick_join_protocol.hpp" + +#include "network/network_manager.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +QuickJoinProtocol::QuickJoinProtocol(CallbackObject* callback_object, uint32_t* server_id) : Protocol(callback_object, PROTOCOL_SILENT) +{ + m_server_id = server_id; +} + +QuickJoinProtocol::~QuickJoinProtocol() +{ +} + +void QuickJoinProtocol::setup() +{ + m_state = NONE; +} + +void QuickJoinProtocol::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("action","quick-join"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + TransportAddress* res = static_cast(m_callback_object); + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + result->get("ip", &res->ip); + result->get("port", &res->port); + result->get("hostid", m_server_id); + Log::info("QuickJoinProtocol", "Quick joining %d:%d (server#%d).", res->ip, res->port, *m_server_id); + } + else + { + Log::error("QuickJoinProtocol", "Fail to quick join."); + } + } + else + { + Log::error("QuickJoinProtocol", "Fail to quick join."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/quick_join_protocol.hpp b/src/network/protocols/quick_join_protocol.hpp new file mode 100644 index 000000000..144e49d20 --- /dev/null +++ b/src/network/protocols/quick_join_protocol.hpp @@ -0,0 +1,32 @@ +#ifndef QUICK_JOIN_PROTOCOL_HPP +#define QUICK_JOIN_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +class QuickJoinProtocol : public Protocol +{ + public: + QuickJoinProtocol(CallbackObject* callback_object, uint32_t* server_id); + virtual ~QuickJoinProtocol(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + uint32_t* m_server_id; + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // QUICK_JOIN_PROTOCOL_HPP diff --git a/src/network/protocols/request_connection.cpp b/src/network/protocols/request_connection.cpp new file mode 100644 index 000000000..f07f43601 --- /dev/null +++ b/src/network/protocols/request_connection.cpp @@ -0,0 +1,93 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/request_connection.hpp" + +#include "network/protocol_manager.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" + +RequestConnection::RequestConnection(uint32_t server_id) : Protocol(NULL, PROTOCOL_SILENT) +{ + m_server_id = server_id; +} + +RequestConnection::~RequestConnection() +{ +} + +void RequestConnection::setup() +{ + m_state = NONE; +} + +void RequestConnection::asynchronousUpdate() +{ + switch (m_state) + { + case NONE: + { + m_request = new Online::CurrentUser::ServerJoinRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("server_id",m_server_id); + m_request->setParameter("action","request-connection"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + break; + } + case REQUEST_PENDING: + { + if (!m_request->isDone()) + return; + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if (rec_success == "yes") + { + Log::debug("RequestConnection", "Connection Request made successfully."); + } + else + { + Log::error("RequestConnection", "Fail to make a request to connecto to server %d", m_server_id); + } + } + else + { + Log::error("RequestConnection", "Fail to make a request."); + } + m_state = DONE; + + break; + } + case DONE: + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + diff --git a/src/network/protocols/request_connection.hpp b/src/network/protocols/request_connection.hpp new file mode 100644 index 000000000..2bffccdd3 --- /dev/null +++ b/src/network/protocols/request_connection.hpp @@ -0,0 +1,33 @@ +#ifndef REQUEST_CONNECTION_HPP +#define REQUEST_CONNECTION_HPP + +#include "network/protocol.hpp" +#include "online/current_user.hpp" + +class RequestConnection : public Protocol +{ + public: + RequestConnection(uint32_t server_id); + virtual ~RequestConnection(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + uint32_t m_server_id; + Online::CurrentUser::ServerJoinRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; + +}; + +#endif // REQUEST_CONNECTION_HPP diff --git a/src/network/protocols/server_lobby_room_protocol.cpp b/src/network/protocols/server_lobby_room_protocol.cpp new file mode 100644 index 000000000..38d4d99fd --- /dev/null +++ b/src/network/protocols/server_lobby_room_protocol.cpp @@ -0,0 +1,659 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/server_lobby_room_protocol.hpp" + +#include "network/server_network_manager.hpp" +#include "network/network_world.hpp" +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/connect_to_peer.hpp" +#include "network/protocols/start_server.hpp" +#include "network/protocols/start_game_protocol.hpp" + +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" +#include "modes/world.hpp" +#include "utils/log.hpp" +#include "utils/time.hpp" +#include "utils/random_generator.hpp" + + +ServerLobbyRoomProtocol::ServerLobbyRoomProtocol() : LobbyRoomProtocol(NULL) +{ +} + +//----------------------------------------------------------------------------- + +ServerLobbyRoomProtocol::~ServerLobbyRoomProtocol() +{ +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::setup() +{ + m_setup = NetworkManager::getInstance()->setupNewGame(); // create a new setup + m_setup->getRaceConfig()->setPlayerCount(16); //FIXME : this has to be moved to when logging into the server + m_next_id = 0; + m_state = NONE; + m_public_address.ip = 0; + m_public_address.port = 0; + m_selection_enabled = false; + m_in_race = false; + Log::info("ServerLobbyRoomProtocol", "Starting the protocol."); +} + +//----------------------------------------------------------------------------- + +bool ServerLobbyRoomProtocol::notifyEventAsynchronous(Event* event) +{ + assert(m_setup); // assert that the setup exists + if (event->type == EVENT_TYPE_MESSAGE) + { + NetworkString data = event->data(); + assert(data.size()); // message not empty + uint8_t message_type; + message_type = data[0]; + event->removeFront(1); + Log::info("ServerLobbyRoomProtocol", "Message received with type %d.", message_type); + if (message_type == 0x01) // player requesting connection + connectionRequested(event); + else if (message_type == 0x02) // player requesting kart selection + kartSelectionRequested(event); + else if (message_type == 0xc0) // vote for major mode + playerMajorVote(event); + else if (message_type == 0xc1) // vote for race count + playerRaceCountVote(event); + else if (message_type == 0xc2) // vote for minor mode + playerMinorVote(event); + else if (message_type == 0xc3) // vote for track + playerTrackVote(event); + else if (message_type == 0xc4) // vote for reversed mode + playerReversedVote(event); + else if (message_type == 0xc5) // vote for laps + playerLapsVote(event); + } // if (event->type == EVENT_TYPE_MESSAGE) + else if (event->type == EVENT_TYPE_CONNECTED) + { + } // if (event->type == EVENT_TYPE_CONNECTED) + else if (event->type == EVENT_TYPE_DISCONNECTED) + { + kartDisconnected(event); + } // if (event->type == EVENT_TYPE_DISCONNECTED) + return true; +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::update() +{ + switch (m_state) + { + case NONE: + m_current_protocol_id = m_listener->requestStart(new GetPublicAddress(&m_public_address)); + m_state = GETTING_PUBLIC_ADDRESS; + break; + case GETTING_PUBLIC_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) == PROTOCOL_STATE_TERMINATED) + { + NetworkManager::getInstance()->setPublicAddress(m_public_address); + m_current_protocol_id = m_listener->requestStart(new StartServer()); + m_state = LAUNCHING_SERVER; + Log::debug("ServerLobbyRoomProtocol", "Public address known."); + } + break; + case LAUNCHING_SERVER: + if (m_listener->getProtocolState(m_current_protocol_id) == PROTOCOL_STATE_TERMINATED) + { + m_state = WORKING; + Log::info("ServerLobbyRoomProtocol", "Server setup"); + } + break; + case WORKING: + { + checkIncomingConnectionRequests(); + if (m_in_race && World::getWorld() && NetworkWorld::getInstance()->isRunning()) + checkRaceFinished(); + + break; + } + case DONE: + m_state = EXITING; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::startGame() +{ + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai8(0x04).ai8(4).ai32(peers[i]->getClientServerToken()); // start game + m_listener->sendMessage(this, peers[i], ns, true); // reliably + } + m_listener->requestStart(new StartGameProtocol(m_setup)); + m_in_race = true; +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::startSelection() +{ + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai8(0x05).ai8(4).ai32(peers[i]->getClientServerToken()); // start selection + m_listener->sendMessage(this, peers[i], ns, true); // reliably + } + m_selection_enabled = true; +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::checkIncomingConnectionRequests() +{ + // first poll every 5 seconds + static double last_poll_time = 0; + if (StkTime::getRealTime() > last_poll_time+10.0) + { + last_poll_time = StkTime::getRealTime(); + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + Online::XMLRequest* request = new Online::XMLRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + request->setParameter("id",Online::CurrentUser::get()->getProfile()->getID()); + request->setParameter("token",Online::CurrentUser::get()->getToken()); + request->setParameter("address",addr.ip); + request->setParameter("port",addr.port); + request->setParameter("action","poll-connection-requests"); + + Online::HTTPManager::get()->synchronousRequest(request); + assert(request->isDone()); + + const XMLNode * result = request->getResult(); + std::string rec_success; + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + const XMLNode * users_xml = result->getNode("users"); + uint32_t id = 0; + for (unsigned int i = 0; i < users_xml->getNumNodes(); i++) + { + users_xml->getNode(i)->get("id", &id); + Log::debug("ServerLobbyRoomProtocol", "User with id %d wants to connect.", id); + m_incoming_peers_ids.push_back(id); + } + } + else + { + Log::error("ServerLobbyRoomProtocol", "Error while reading the list."); + } + } + else + { + Log::error("ServerLobbyRoomProtocol", "Cannot retrieve the list."); + } + delete request; + } + + // now + for (unsigned int i = 0; i < m_incoming_peers_ids.size(); i++) + { + m_listener->requestStart(new ConnectToPeer(m_incoming_peers_ids[i])); + } + m_incoming_peers_ids.clear(); +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::checkRaceFinished() +{ + assert(NetworkWorld::getInstance()->isRunning()); + assert(World::getWorld()); + // if race is over, give the final score to everybody + if (NetworkWorld::getInstance()->isRaceOver()) + { + // calculate karts ranks : + int num_players = race_manager->getNumberOfKarts(); + std::vector karts_results; + std::vector karts_times; + for (int j = 0; j < num_players; j++) + { + float kart_time = race_manager->getKartRaceTime(j); + for (unsigned int i = 0; i < karts_times.size(); i++) + { + if (kart_time < karts_times[i]) + { + karts_times.insert(karts_times.begin()+i, kart_time); + karts_results.insert(karts_results.begin()+i, j); + break; + } + } + } + + std::vector peers = NetworkManager::getInstance()->getPeers(); + + NetworkString queue; + for (unsigned int i = 0; i < karts_results.size(); i++) + { + queue.ai8(1).ai8(karts_results[i]); // kart pos = i+1 + Log::info("ServerLobbyRoomProtocol", "Kart %d finished #%d", karts_results[i], i+1); + } + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai8(0x06).ai8(4).ai32(peers[i]->getClientServerToken()); + NetworkString total = ns + queue; + m_listener->sendMessage(this, peers[i], total, true); + } + Log::info("ServerLobbyRoomProtocol", "End of game message sent"); + m_in_race = false; + + // stop race protocols + Protocol* protocol = NULL; + protocol = m_listener->getProtocol(PROTOCOL_CONTROLLER_EVENTS); + if (protocol) + m_listener->requestTerminate(protocol); + else + Log::error("ClientLobbyRoomProtocol", "No controller events protocol registered."); + + protocol = m_listener->getProtocol(PROTOCOL_KART_UPDATE); + if (protocol) + m_listener->requestTerminate(protocol); + else + Log::error("ClientLobbyRoomProtocol", "No kart update protocol registered."); + + protocol = m_listener->getProtocol(PROTOCOL_GAME_EVENTS); + if (protocol) + m_listener->requestTerminate(protocol); + else + Log::error("ClientLobbyRoomProtocol", "No game events protocol registered."); + + // notify the network world that it is stopped + NetworkWorld::getInstance()->stop(); + // exit the race now + race_manager->exitRace(); + race_manager->setAIKartOverride(""); + } + else + { + //Log::info("ServerLobbyRoomProtocol", "Phase is %d", World::getWorld()->getPhase()); + } +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::kartDisconnected(Event* event) +{ + STKPeer* peer = *(event->peer); + if (peer->getPlayerProfile() != NULL) // others knew him + { + NetworkString msg; + msg.ai8(0x02).ai8(1).ai8(peer->getPlayerProfile()->race_id); + m_listener->sendMessage(this, msg); + Log::info("ServerLobbyRoomProtocol", "Player disconnected : id %d", + peer->getPlayerProfile()->race_id); + m_setup->removePlayer(peer->getPlayerProfile()->race_id); + NetworkManager::getInstance()->removePeer(peer); + } + else + Log::info("ServerLobbyRoomProtocol", "The DC peer wasn't registered."); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a player asks for a connection. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 + * ------------------------ + * Size | 1 | 4 | + * Data | 4 | global player id | + * ------------------------ + */ +void ServerLobbyRoomProtocol::connectionRequested(Event* event) +{ + STKPeer* peer = *(event->peer); + NetworkString data = event->data(); + if (data.size() != 5 || data[0] != 4) + { + Log::warn("ServerLobbyRoomProtocol", "Receiving badly formated message. Size is %d and first byte %d", data.size(), data[0]); + return; + } + uint32_t player_id = 0; + player_id = data.getUInt32(1); + // can we add the player ? + if (m_setup->getPlayerCount() < + ServerNetworkManager::getInstance()->getMaxPlayers()) //accept + { + // add the player to the game setup + m_next_id = m_setup->getPlayerCount(); + // notify everybody that there is a new player + NetworkString message; + // new player (1) -- size of id -- id -- size of local id -- local id; + message.ai8(1).ai8(4).ai32(player_id).ai8(1).ai8(m_next_id); + m_listener->sendMessageExcept(this, peer, message); + + /// now answer to the peer that just connected + RandomGenerator token_generator; + // use 4 random numbers because rand_max is probably 2^15-1. + uint32_t token = (uint32_t)(((token_generator.get(RAND_MAX)<<24) & 0xff) + + ((token_generator.get(RAND_MAX)<<16) & 0xff) + + ((token_generator.get(RAND_MAX)<<8) & 0xff) + + ((token_generator.get(RAND_MAX) & 0xff))); + + // send a message to the one that asked to connect + NetworkString message_ack; + // connection success (129) -- size of token -- token + message_ack.ai8(0x81).ai8(1).ai8(m_next_id).ai8(4).ai32(token).ai8(4).ai32(player_id); + // add all players so that this user knows + std::vector players = m_setup->getPlayers(); + for (unsigned int i = 0; i < players.size(); i++) + { + // do not duplicate the player into the message + if (players[i]->race_id != m_next_id && players[i]->user_profile->getID() != player_id) + message_ack.ai8(1).ai8(players[i]->race_id).ai8(4).ai32(players[i]->user_profile->getID()); + } + m_listener->sendMessage(this, peer, message_ack); + + peer->setClientServerToken(token); + + NetworkPlayerProfile* profile = new NetworkPlayerProfile(); + profile->race_id = m_next_id; + profile->kart_name = ""; + profile->user_profile = new Online::Profile(player_id, ""); + m_setup->addPlayer(profile); + peer->setPlayerProfile(profile); + Log::verbose("ServerLobbyRoomProtocol", "New player."); + } // accept player + else // refuse the connection with code 0 (too much players) + { + NetworkString message; + message.ai8(0x80); // 128 means connection refused + message.ai8(1); // 1 bytes for the error code + message.ai8(0); // 0 = too much players + // send only to the peer that made the request + m_listener->sendMessage(this, peer, message); + Log::verbose("ServerLobbyRoomProtocol", "Player refused"); + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a player asks to select a kart. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 N+6 + * --------------------------------------------------- + * Size | 1 | 4 | 1 | N | + * Data | 4 | priv token | N (kart name size) | kart name | + * --------------------------------------------------- + */ +void ServerLobbyRoomProtocol::kartSelectionRequested(Event* event) +{ + NetworkString data = event->data(); + STKPeer* peer = *(event->peer); + if (!checkDataSizeAndToken(event, 6)) + return; + + uint8_t kart_name_size = data.gui8(5); + std::string kart_name = data.gs(6, kart_name_size); + if (kart_name.size() != kart_name_size) + { + Log::error("ServerLobbyRoomProtocol", "Kart names sizes differ: told:" + "%d, real: %d.", kart_name_size, kart_name.size()); + return; + } + // check if selection is possible + if (!m_selection_enabled) + { + NetworkString answer; + answer.ai8(0x82).ai8(1).ai8(2); // selection still not started + m_listener->sendMessage(this, peer, answer); + return; + } + // check if somebody picked that kart + if (!m_setup->isKartAvailable(kart_name)) + { + NetworkString answer; + answer.ai8(0x82).ai8(1).ai8(0); // kart is already taken + m_listener->sendMessage(this, peer, answer); + return; + } + // check if this kart is authorized + if (!m_setup->isKartAllowed(kart_name)) + { + NetworkString answer; + answer.ai8(0x82).ai8(1).ai8(1); // kart is not authorized + m_listener->sendMessage(this, peer, answer); + return; + } + // send a kart update to everyone + NetworkString answer; + // kart update (3), 1, race id + answer.ai8(0x03).ai8(1).ai8(peer->getPlayerProfile()->race_id); + // kart name size, kart name + answer.ai8(kart_name.size()).as(kart_name); + m_listener->sendMessage(this, answer); + m_setup->setPlayerKart(peer->getPlayerProfile()->race_id, kart_name); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a major race mode. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 + * ---------------------------------------- + * Size | 1 | 4 | 1 | 1 | + * Data | 4 | priv token | 1 | major mode vote | + * ---------------------------------------- + */ +void ServerLobbyRoomProtocol::playerMajorVote(Event* event) +{ + NetworkString data = event->data(); + STKPeer* peer = *(event->peer); + if (!checkDataSizeAndToken(event, 7)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + uint8_t player_id = peer->getPlayerProfile()->race_id; + m_setup->getRaceConfig()->setPlayerMajorVote(player_id, data[6]); + // Send the vote to everybody (including the sender) + NetworkString other; + other.ai8(1).ai8(player_id); // add the player id + data.removeFront(5); // remove the token + other += data; // add the data + NetworkString prefix; + prefix.ai8(0xc0); // prefix the token with the ype + sendMessageToPeersChangingToken(prefix, other); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for the number of races in a GP. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 + * ------------------------------------ + * Size | 1 | 4 | 1 | 1 | + * Data | 4 | priv token | 1 | races count | + * ------------------------------------ + */ +void ServerLobbyRoomProtocol::playerRaceCountVote(Event* event) +{ + NetworkString data = event->data(); + STKPeer* peer = *(event->peer); + if (!checkDataSizeAndToken(event, 7)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + uint8_t player_id = peer->getPlayerProfile()->race_id; + m_setup->getRaceConfig()->setPlayerRaceCountVote(player_id, data[6]); + // Send the vote to everybody (including the sender) + NetworkString other; + other.ai8(1).ai8(player_id); // add the player id + data.removeFront(5); // remove the token + other += data; // add the data + NetworkString prefix; + prefix.ai8(0xc1); // prefix the token with the type + sendMessageToPeersChangingToken(prefix, other); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a minor race mode. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 + * ---------------------------------------- + * Size | 1 | 4 | 1 | 1 | + * Data | 4 | priv token | 1 | minor mode vote | + * ---------------------------------------- + */ +void ServerLobbyRoomProtocol::playerMinorVote(Event* event) +{ + NetworkString data = event->data(); + STKPeer* peer = *(event->peer); + if (!checkDataSizeAndToken(event, 7)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + uint8_t player_id = peer->getPlayerProfile()->race_id; + m_setup->getRaceConfig()->setPlayerMinorVote(player_id, data[6]); + // Send the vote to everybody (including the sender) + NetworkString other; + other.ai8(1).ai8(player_id); // add the player id + data.removeFront(5); // remove the token + other += data; // add the data + NetworkString prefix; + prefix.ai8(0xc2); // prefix the token with the ype + sendMessageToPeersChangingToken(prefix, other); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a track. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 N+6 N+7 N+8 + * ----------------------------------------------------------- + * Size | 1 | 4 | 1 | N | 1 | 1 | + * Data | 4 | priv token | N | track name | 1 | track number (gp) | + * ----------------------------------------------------------- + */ +void ServerLobbyRoomProtocol::playerTrackVote(Event* event) +{ + NetworkString data = event->data(); + STKPeer* peer = *(event->peer); + if (!checkDataSizeAndToken(event, 8)) + return; + int N = data[5]; + std::string track_name = data.gs(5, N); + if (!isByteCorrect(event, N+6, 1)) + return; + uint8_t player_id = peer->getPlayerProfile()->race_id; + m_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name, data[N+7]); + // Send the vote to everybody (including the sender) + NetworkString other; + other.ai8(1).ai8(player_id); // add the player id + data.removeFront(5); // remove the token + other += data; // add the data + NetworkString prefix; + prefix.ai8(0xc3); // prefix the token with the ype + sendMessageToPeersChangingToken(prefix, other); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for the reverse mode of a race + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 + * --------------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | + * Data | 4 | priv token | 1 | reversed | 1 | track number (gp) | + * --------------------------------------------------------- + */ +void ServerLobbyRoomProtocol::playerReversedVote(Event* event) +{ + NetworkString data = event->data(); + STKPeer* peer = *(event->peer); + if (!checkDataSizeAndToken(event, 9)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + if (!isByteCorrect(event, 7, 1)) + return; + uint8_t player_id = peer->getPlayerProfile()->race_id; + m_setup->getRaceConfig()->setPlayerReversedVote(player_id, data[6]!=0, data[8]); + // Send the vote to everybody (including the sender) + NetworkString other; + other.ai8(1).ai8(player_id); // add the player id + data.removeFront(5); // remove the token + other += data; // add the data + NetworkString prefix; + prefix.ai8(0xc4); // prefix the token with the ype + sendMessageToPeersChangingToken(prefix, other); +} +//----------------------------------------------------------------------------- + +/*! \brief Called when a player votes for a major race mode. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 8 9 + * ----------------------------------------------------- + * Size | 1 | 4 | 1 | 1 | 1 | 1 | + * Data | 4 | priv token | 1 | laps | 1 | track number (gp) | + * ----------------------------------------------------- + */ +void ServerLobbyRoomProtocol::playerLapsVote(Event* event) +{ + NetworkString data = event->data(); + STKPeer* peer = *(event->peer); + if (!checkDataSizeAndToken(event, 9)) + return; + if (!isByteCorrect(event, 5, 1)) + return; + if (!isByteCorrect(event, 7, 1)) + return; + uint8_t player_id = peer->getPlayerProfile()->race_id; + m_setup->getRaceConfig()->setPlayerLapsVote(player_id, data[6], data[8]); + // Send the vote to everybody (including the sender) + NetworkString other; + other.ai8(1).ai8(player_id); // add the player id + data.removeFront(5); // remove the token + other += data; // add the data + NetworkString prefix; + prefix.ai8(0xc5); // prefix the token with the ype + sendMessageToPeersChangingToken(prefix, other); +} +//----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby_room_protocol.hpp b/src/network/protocols/server_lobby_room_protocol.hpp new file mode 100644 index 000000000..82d2691f0 --- /dev/null +++ b/src/network/protocols/server_lobby_room_protocol.hpp @@ -0,0 +1,55 @@ +#ifndef SERVER_LOBBY_ROOM_PROTOCOL_HPP +#define SERVER_LOBBY_ROOM_PROTOCOL_HPP + +#include "network/protocols/lobby_room_protocol.hpp" + +class ServerLobbyRoomProtocol : public LobbyRoomProtocol +{ + public: + ServerLobbyRoomProtocol(); + virtual ~ServerLobbyRoomProtocol(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {}; + + void startGame(); + void startSelection(); + void checkIncomingConnectionRequests(); + void checkRaceFinished(); + + protected: + // connection management + void kartDisconnected(Event* event); + void connectionRequested(Event* event); + // kart selection + void kartSelectionRequested(Event* event); + // race votes + void playerMajorVote(Event* event); + void playerRaceCountVote(Event* event); + void playerMinorVote(Event* event); + void playerTrackVote(Event* event); + void playerReversedVote(Event* event); + void playerLapsVote(Event* event); + + uint8_t m_next_id; //!< Next id to assign to a peer. + std::vector m_peers; + std::vector m_incoming_peers_ids; + uint32_t m_current_protocol_id; + TransportAddress m_public_address; + bool m_selection_enabled; + bool m_in_race; + + enum STATE + { + NONE, + GETTING_PUBLIC_ADDRESS, + LAUNCHING_SERVER, + WORKING, + DONE, + EXITING + }; + STATE m_state; +}; +#endif // SERVER_LOBBY_ROOM_PROTOCOL_HPP diff --git a/src/network/protocols/show_public_address.cpp b/src/network/protocols/show_public_address.cpp new file mode 100644 index 000000000..0a580e955 --- /dev/null +++ b/src/network/protocols/show_public_address.cpp @@ -0,0 +1,87 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/show_public_address.hpp" + +#include "network/network_manager.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +ShowPublicAddress::ShowPublicAddress() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +ShowPublicAddress::~ShowPublicAddress() +{ +} + +void ShowPublicAddress::setup() +{ + m_state = NONE; +} + +void ShowPublicAddress::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("address",addr.ip); + m_request->setParameter("port",addr.port); + m_request->setParameter("private_port",NetworkManager::getInstance()->getHost()->getPort()); + m_request->setParameter("action","set"); + Log::info("ShowPublicAddress", "Showing addr %u and port %d", addr.ip, addr.port); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::debug("ShowPublicAddress", "Address shown successfully."); + } + else + { + Log::error("ShowPublicAddress", "Fail to show address."); + } + } + else + { + Log::error("ShowPublicAddress", "Fail to show address."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/show_public_address.hpp b/src/network/protocols/show_public_address.hpp new file mode 100644 index 000000000..fb84f8477 --- /dev/null +++ b/src/network/protocols/show_public_address.hpp @@ -0,0 +1,50 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 SHOW_PUBLIC_ADDRESS_HPP +#define SHOW_PUBLIC_ADDRESS_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" +#include + +class ShowPublicAddress : public Protocol +{ + public: + ShowPublicAddress(); + virtual ~ShowPublicAddress(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/start_game_protocol.cpp b/src/network/protocols/start_game_protocol.cpp new file mode 100644 index 000000000..a29e15d89 --- /dev/null +++ b/src/network/protocols/start_game_protocol.cpp @@ -0,0 +1,207 @@ +#include "network/protocols/start_game_protocol.hpp" + +#include "input/device_manager.hpp" +#include "input/input_manager.hpp" +#include "challenges/unlock_manager.hpp" +#include "modes/world.hpp" +#include "network/network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/game_setup.hpp" +#include "network/network_world.hpp" +#include "network/protocols/synchronization_protocol.hpp" +#include "online/current_user.hpp" +#include "race/race_manager.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/kart_selection.hpp" +#include "states_screens/network_kart_selection.hpp" +#include "utils/log.hpp" +#include "utils/time.hpp" + +StartGameProtocol::StartGameProtocol(GameSetup* game_setup) : + Protocol(NULL, PROTOCOL_START_GAME) +{ + m_game_setup = game_setup; + std::vector players = m_game_setup->getPlayers(); + for (unsigned int i = 0; i < players.size(); i++) + { + m_player_states.insert(std::pair(players[i], LOADING)); + } + m_ready_count = 0; +} + +StartGameProtocol::~StartGameProtocol() +{ +} + +bool StartGameProtocol::notifyEventAsynchronous(Event* event) +{ + NetworkString data = event->data(); + if (data.size() < 5) + { + Log::error("StartGameProtocol", "Too short message."); + return true; + } + uint32_t token = data.gui32(); + uint8_t ready = data.gui8(4); + STKPeer* peer = (*(event->peer)); + if (peer->getClientServerToken() != token) + { + Log::error("StartGameProtocol", "Bad token received."); + return true; + } + if (m_listener->isServer() && ready) // on server, player is ready + { + Log::info("StartGameProtocol", "One of the players is ready."); + m_player_states[peer->getPlayerProfile()] = READY; + m_ready_count++; + if (m_ready_count == m_game_setup->getPlayerCount()) + { + // everybody ready, synchronize + SynchronizationProtocol* protocol = static_cast(m_listener->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (protocol) + { + protocol->startCountdown(5000); // 5 seconds countdown + Log::info("StartGameProtocol", "All players ready, starting countdown."); + m_ready = true; + return true; + } + else + Log::error("StartGameProtocol", "The Synchronization protocol hasn't been started."); + } + } + else // on the client, we shouldn't even receive messages. + { + Log::error("StartGameProtocol", "Received a message with bad format."); + } + return true; +} + +void StartGameProtocol::setup() +{ + m_state = NONE; + m_ready_count = 0; + m_ready = false; + Log::info("SynchronizationProtocol", "Ready !"); +} + +bool sort_karts (NetworkPlayerProfile* a, NetworkPlayerProfile* b) +{ return (a->race_id < b->race_id); } + +void StartGameProtocol::update() +{ + if (m_state == NONE) + { + // if no synchronization protocol exists, create one + m_listener->requestStart(new SynchronizationProtocol()); + Log::info("StartGameProtocol", "SynchronizationProtocol started."); + // race startup sequence + NetworkWorld::getInstance()->start(); // builds it and starts + race_manager->setNumKarts(m_game_setup->getPlayerCount()); + race_manager->setNumPlayers(m_game_setup->getPlayerCount()); + race_manager->setNumLocalPlayers(1); + std::vector players = m_game_setup->getPlayers(); + std::sort(players.begin(), players.end(), sort_karts); + // have to add self first + for (unsigned int i = 0; i < players.size(); i++) + { + bool is_me = (players[i]->user_profile == Online::CurrentUser::get()->getProfile()); + if (is_me) + { + NetworkPlayerProfile* profile = players[i]; + RemoteKartInfo rki(profile->race_id, profile->kart_name, + profile->user_profile->getUserName(), profile->race_id, !is_me); + rki.setGlobalPlayerId(profile->race_id); + rki.setLocalPlayerId(is_me?0:1); + rki.setHostId(profile->race_id); + PlayerProfile* profileToUse = unlock_manager->getCurrentPlayer(); + assert(profileToUse); + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + int new_player_id = 0; + if (StateManager::get()->getActivePlayers().size() >= 0) // more than one player, we're the first + new_player_id = 0; + else + new_player_id = StateManager::get()->createActivePlayer( profileToUse, device , players[i]->user_profile); + device->setPlayer(StateManager::get()->getActivePlayer(new_player_id)); + input_manager->getDeviceList()->setSinglePlayer(StateManager::get()->getActivePlayer(new_player_id)); + + race_manager->setPlayerKart(i, rki); + race_manager->setLocalKartInfo(new_player_id, profile->kart_name); + Log::info("StartGameProtocol", "Self player device added."); // self config + NetworkWorld::getInstance()->m_self_kart = profile->kart_name; + } + } + for (unsigned int i = 0; i < players.size(); i++) + { + bool is_me = (players[i]->user_profile == Online::CurrentUser::get()->getProfile()); + NetworkPlayerProfile* profile = players[i]; + RemoteKartInfo rki(profile->race_id, profile->kart_name, + profile->user_profile->getUserName(), profile->race_id, !is_me); + rki.setGlobalPlayerId(profile->race_id); + // on the server, the race id must be the local one. + rki.setLocalPlayerId(m_listener->isServer()?profile->race_id:(is_me?0:1)); + rki.setHostId(profile->race_id); + Log::info("StartGameProtocol", "Creating kart %s for Player#%d with race_id %d", profile->kart_name.c_str(), i, profile->race_id); + + if (!is_me) + { + StateManager::get()->createActivePlayer( NULL, NULL , players[i]->user_profile); + + race_manager->setPlayerKart(i, rki); + } + } + race_manager->computeRandomKartList(); + Log::info("StartGameProtocol", "Player configuration ready."); + m_state = SYNCHRONIZATION_WAIT; +/* + KartSelectionScreen* s = KartSelectionScreen::getInstance(); + s->setMultiplayer(false); + s->setFromOverworld(false); + StateManager::get()->pushScreen( s );*/ + } + else if (m_state == SYNCHRONIZATION_WAIT) + { + SynchronizationProtocol* protocol = static_cast + (m_listener->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (protocol) + { + // now the synchronization protocol exists. + Log::info("StartGameProtocol", "Starting the race loading."); + race_manager->startSingleRace("jungle", 1, false); + World::getWorld()->setNetworkWorld(true); + m_state = LOADING; + } + } + else if (m_state == LOADING) + { + if (m_ready) + { + m_state = READY; + } + } + else if (m_state == READY) + { + // set karts into the network game setup + NetworkManager::getInstance()->getGameSetup()->bindKartsToProfiles(); + m_state = EXITING; + m_listener->requestTerminate(this); + } +} + +void StartGameProtocol::ready() // on clients, means the loading is finished +{ + if (!m_listener->isServer()) // if we're a client + { + assert(NetworkManager::getInstance()->getPeerCount() == 1); + NetworkString ns; + ns.ai32(NetworkManager::getInstance()->getPeers()[0]->getClientServerToken()).ai8(1); + Log::info("StartGameProtocol", "Player ready, notifying server."); + m_listener->sendMessage(this, ns, true); + m_state = READY; + m_ready = true; + return; + } + else // on the server + { + } +} + diff --git a/src/network/protocols/start_game_protocol.hpp b/src/network/protocols/start_game_protocol.hpp new file mode 100644 index 000000000..f58f71aae --- /dev/null +++ b/src/network/protocols/start_game_protocol.hpp @@ -0,0 +1,36 @@ +#ifndef START_GAME_PROTOCOL_HPP +#define START_GAME_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include + +class GameSetup; +class NetworkPlayerProfile; + +class StartGameProtocol : public Protocol +{ + protected: + enum STATE { NONE, SYNCHRONIZATION_WAIT, LOADING, READY, EXITING }; + std::map m_player_states; + + GameSetup* m_game_setup; + int m_ready_count; + double m_sending_time; + + STATE m_state; + bool m_ready; + + public: + StartGameProtocol(GameSetup* game_setup); + virtual ~StartGameProtocol(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {} + + void ready(); + +}; + +#endif // START_GAME_PROTOCOL_HPP diff --git a/src/network/protocols/start_server.cpp b/src/network/protocols/start_server.cpp new file mode 100644 index 000000000..554f9735f --- /dev/null +++ b/src/network/protocols/start_server.cpp @@ -0,0 +1,87 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/start_server.hpp" + +#include "network/network_manager.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" + +StartServer::StartServer() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +StartServer::~StartServer() +{ +} + +void StartServer::setup() +{ + m_state = NONE; +} + +void StartServer::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("address",addr.ip); + m_request->setParameter("port",addr.port); + m_request->setParameter("private_port",NetworkManager::getInstance()->getHost()->getPort()); + m_request->setParameter("max_players",UserConfigParams::m_server_max_players); + m_request->setParameter("action","start-server"); + Log::info("ShowPublicAddress", "Showing addr %u and port %d", addr.ip, addr.port); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::info("StartServer", "Server is now online."); + } + else + { + Log::error("StartServer", "Fail to start server."); + } + } + else + { + Log::error("StartServer", "Fail to start server."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/start_server.hpp b/src/network/protocols/start_server.hpp new file mode 100644 index 000000000..5fce8a458 --- /dev/null +++ b/src/network/protocols/start_server.hpp @@ -0,0 +1,35 @@ +#ifndef START_SERVER_HPP +#define START_SERVER_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +/*! + * This protocol tells to the database that the server is up and running, + * and shows online the public IP:port that stores the NetworkManager. + */ +class StartServer : public Protocol +{ + public: + StartServer(); + virtual ~StartServer(); + + virtual bool notifyEvent(Event* event) { return true; } + virtual bool notifyEventAsynchronous(Event* event) { return true; } + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // START_SERVER_HPP diff --git a/src/network/protocols/stop_server.cpp b/src/network/protocols/stop_server.cpp new file mode 100644 index 000000000..e98b2fb20 --- /dev/null +++ b/src/network/protocols/stop_server.cpp @@ -0,0 +1,90 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/protocols/stop_server.hpp" + +#include "network/network_manager.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" + +StopServer::StopServer() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +StopServer::~StopServer() +{ +} + +bool StopServer::notifyEventAsynchronous(Event* event) +{ + return true; +} + +void StopServer::setup() +{ + m_state = NONE; +} + +void StopServer::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("address",addr.ip); + m_request->setParameter("port",addr.port); + m_request->setParameter("action","stop-server"); + Log::info("StopServer", "address %u, port %d", addr.ip, addr.port); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::info("StopServer", "Server is now offline."); + } + else + { + Log::error("StopServer", "Fail to stop server."); + } + } + else + { + Log::error("StopServer", "Fail to stop server."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/stop_server.hpp b/src/network/protocols/stop_server.hpp new file mode 100644 index 000000000..5b5c7a553 --- /dev/null +++ b/src/network/protocols/stop_server.hpp @@ -0,0 +1,33 @@ +#ifndef STOP_SERVER_HPP +#define STOP_SERVER_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +/*! \brief Removes the server info from the database + */ + +class StopServer : public Protocol +{ + public: + StopServer(); + virtual ~StopServer(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // STOP_SERVER_HPP diff --git a/src/network/protocols/synchronization_protocol.cpp b/src/network/protocols/synchronization_protocol.cpp new file mode 100644 index 000000000..23eb8921f --- /dev/null +++ b/src/network/protocols/synchronization_protocol.cpp @@ -0,0 +1,183 @@ +#include "network/protocols/synchronization_protocol.hpp" + +#include "network/network_manager.hpp" +#include "network/protocols/kart_update_protocol.hpp" +#include "network/protocols/controller_events_protocol.hpp" +#include "network/protocols/game_events_protocol.hpp" +#include "utils/time.hpp" + +//----------------------------------------------------------------------------- + +SynchronizationProtocol::SynchronizationProtocol() : Protocol(NULL, PROTOCOL_SYNCHRONIZATION) +{ + unsigned int size = NetworkManager::getInstance()->getPeerCount(); + m_pings.resize(size, std::map()); + m_pings_count.resize(size); + for (unsigned int i = 0; i < size; i++) + { + m_pings_count[i] = 0; + } + m_successed_pings.resize(size); + m_total_diff.resize(size); + m_average_ping.resize(size); + m_countdown_activated = false; +} + +//----------------------------------------------------------------------------- + +SynchronizationProtocol::~SynchronizationProtocol() +{ +} + +//----------------------------------------------------------------------------- + +bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) +{ + if (event->type != EVENT_TYPE_MESSAGE) + return true; + NetworkString data = event->data(); + if (data.size() < 10) + { + Log::warn("SynchronizationProtocol", "Received a message too short."); + return true; + } + uint8_t talk_id = data.gui8(); + uint32_t token = data.gui32(1); + uint32_t request = data.gui8(5); + uint32_t sequence = data.gui32(6); + + std::vector peers = NetworkManager::getInstance()->getPeers(); + + if (m_listener->isServer()) + { + if (talk_id > peers.size()) + { + Log::warn("SynchronizationProtocol", "The ID isn't known."); + return true; + } + } + + uint8_t peer_id; + for (unsigned int i = 0; i < peers.size(); i++) + { + if (peers[i]->isSamePeer(*event->peer)) + { + peer_id = i; + } + } + if (peers[peer_id]->getClientServerToken() != token) + { + Log::warn("SynchronizationProtocol", "Bad token from peer %d", talk_id); + return true; + } + + if (request) + { + NetworkString response; + response.ai8(data.gui8(talk_id)).ai32(token).ai8(0).ai32(sequence); + m_listener->sendMessage(this, peers[peer_id], response, false); + Log::verbose("SynchronizationProtocol", "Answering sequence %u", sequence); + if (data.size() == 14 && !m_listener->isServer()) // countdown time in the message + { + uint32_t time_to_start = data.gui32(10); + Log::debug("SynchronizationProtocol", "Request to start game in %d.", time_to_start); + if (!m_countdown_activated) + startCountdown(time_to_start); + else + m_countdown = (double)(time_to_start/1000.0); + } + else + Log::verbose("SynchronizationProtocol", "No countdown for now."); + } + else // response + { + if (sequence >= m_pings[peer_id].size()) + { + Log::warn("SynchronizationProtocol", "The sequence# %u isn't known.", sequence); + return true; + } + double current_time = StkTime::getRealTime(); + m_total_diff[peer_id] += current_time - m_pings[peer_id][sequence]; + Log::verbose("SynchronizationProtocol", "InstantPing is %u", + (unsigned int)((current_time - m_pings[peer_id][sequence])*1000)); + m_successed_pings[peer_id]++; + m_average_ping[peer_id] = (int)((m_total_diff[peer_id]/m_successed_pings[peer_id])*1000.0); + + Log::debug("SynchronizationProtocol", "Ping is %u", m_average_ping[peer_id]); + } + return true; +} + +//----------------------------------------------------------------------------- + +void SynchronizationProtocol::setup() +{ + Log::info("SynchronizationProtocol", "Ready !"); + m_countdown = 5.0; // init the countdown to 5s + m_has_quit = false; +} + +//----------------------------------------------------------------------------- + +void SynchronizationProtocol::asynchronousUpdate() +{ + static double timer = StkTime::getRealTime(); + double current_time = StkTime::getRealTime(); + if (m_countdown_activated) + { + m_countdown -= (current_time - m_last_countdown_update); + m_last_countdown_update = current_time; + Log::debug("SynchronizationProtocol", "Update! Countdown remaining : %f", m_countdown); + if (m_countdown < 0.0 && !m_has_quit) + { + m_has_quit = true; + Log::info("SynchronizationProtocol", "Countdown finished. Starting now."); + m_listener->requestStart(new KartUpdateProtocol()); + m_listener->requestStart(new ControllerEventsProtocol()); + m_listener->requestStart(new GameEventsProtocol()); + m_listener->requestTerminate(this); + return; + } + static int seconds = -1; + if (seconds == -1) + { + seconds = (int)(ceil(m_countdown)); + } + else if (seconds != (int)(ceil(m_countdown))) + { + seconds = (int)(ceil(m_countdown)); + Log::info("SynchronizationProtocol", "Starting in %d seconds.", seconds); + } + } + if (current_time > timer+0.1) + { + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai8(i).addUInt32(peers[i]->getClientServerToken()).addUInt8(1).addUInt32(m_pings[i].size()); + // now add the countdown if necessary + if (m_countdown_activated && m_listener->isServer()) + { + ns.addUInt32((int)(m_countdown*1000.0)); + Log::debug("SynchronizationProtocol", "CNTActivated: Countdown value : %f", m_countdown); + } + Log::verbose("SynchronizationProtocol", "Added sequence number %u for peer %d", m_pings[i].size(), i); + timer = current_time; + m_pings[i].insert(std::pair(m_pings_count[i], timer)); + m_listener->sendMessage(this, peers[i], ns, false); + m_pings_count[i]++; + } + } + +} + +//----------------------------------------------------------------------------- + +void SynchronizationProtocol::startCountdown(int ms_countdown) +{ + m_countdown_activated = true; + m_countdown = (double)(ms_countdown)/1000.0; + m_last_countdown_update = StkTime::getRealTime(); + Log::info("SynchronizationProtocol", "Countdown started with value %f", m_countdown); +} diff --git a/src/network/protocols/synchronization_protocol.hpp b/src/network/protocols/synchronization_protocol.hpp new file mode 100644 index 000000000..c04afb829 --- /dev/null +++ b/src/network/protocols/synchronization_protocol.hpp @@ -0,0 +1,35 @@ +#ifndef SYNCHRONIZATION_PROTOCOL_HPP +#define SYNCHRONIZATION_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include +#include + +class SynchronizationProtocol : public Protocol +{ + public: + SynchronizationProtocol(); + virtual ~SynchronizationProtocol(); + + virtual bool notifyEventAsynchronous(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + void startCountdown(int ms_countdown); + + int getCountdown() { return (int)(m_countdown*1000.0); } + + protected: + std::vector > m_pings; + std::vector m_average_ping; + std::vector m_pings_count; + std::vector m_successed_pings; + std::vector m_total_diff; + bool m_countdown_activated; + double m_countdown; + double m_last_countdown_update; + bool m_has_quit; +}; + +#endif // SYNCHRONIZATION_PROTOCOL_HPP diff --git a/src/network/race_config.cpp b/src/network/race_config.cpp new file mode 100644 index 000000000..de0f756fe --- /dev/null +++ b/src/network/race_config.cpp @@ -0,0 +1,369 @@ +#include "network/race_config.hpp" + +#include "race/race_manager.hpp" +#include "utils/log.hpp" + +/** \brief Gets the element with the highest count in a std::map. + * \param histogram : A pointer to the histogram. + * \return The key of type S that has the highest second value. + */ +template +S getHighestInHistogram(std::map* histogram) +{ + S best_item; + uint8_t highest_count = histogram->begin()->second; + for (typename std::map::iterator it = histogram->begin(); + it != histogram->end(); it++) + { + if (it->second > highest_count) + { + highest_count = it->second; + best_item = it->first; + } + } + return best_item; +} + +//----------------------------------------------------------------------------- +//--------------------------------- TrackVote -------------------------------- +//----------------------------------------------------------------------------- + +TrackVote::TrackVote() +{ + has_voted_laps = false; + has_voted_track = false; + has_voted_reversed = false; +} +//----------------------------------------------------------------------------- +void TrackVote::voteTrack(std::string track) +{ + track_info.track = track; + has_voted_track = true; +} +//----------------------------------------------------------------------------- +void TrackVote::voteReversed(bool reversed) +{ + track_info.reversed = reversed; + has_voted_reversed = true; +} +//----------------------------------------------------------------------------- +void TrackVote::voteLaps(uint8_t laps) +{ + track_info.laps = laps; + has_voted_laps = true; +} + +//----------------------------------------------------------------------------- +//--------------------------------- RaceVote --------------------------------- +//----------------------------------------------------------------------------- + +RaceVote::RaceVote() +{ + m_has_voted_major = false; + m_has_voted_minor = false; + m_has_voted_races_count = false; + m_major_mode = 0; + m_minor_mode = 0; + m_races_count = 0; +} + +//----------------------------------------------------------------------------- + +void RaceVote::voteMajor(uint8_t major) +{ + m_has_voted_major = true; + m_major_mode = major; +} + +//----------------------------------------------------------------------------- + +void RaceVote::voteRaceCount(uint8_t count) +{ + m_has_voted_races_count = true; + m_races_count = count; +} + +//----------------------------------------------------------------------------- + +void RaceVote::voteMinor(uint8_t minor) +{ + m_has_voted_minor = true; + m_minor_mode = minor; +} + +//----------------------------------------------------------------------------- + +void RaceVote::voteTrack(std::string track, uint8_t track_number) +{ + m_tracks_vote[track_number].voteTrack(track); +} + +//----------------------------------------------------------------------------- + +void RaceVote::voteReversed(bool reversed, uint8_t track_number) +{ + m_tracks_vote[track_number].voteReversed(reversed); +} + +//----------------------------------------------------------------------------- + +void RaceVote::voteLaps(uint8_t laps, uint8_t track_number) +{ + m_tracks_vote[track_number].voteLaps(laps); +} + +//----------------------------------------------------------------------------- +bool RaceVote::hasVotedMajor() const +{ + return m_has_voted_major; +} +//----------------------------------------------------------------------------- +bool RaceVote::hasVotedRacesCount() const +{ + return m_has_voted_races_count; +} +//----------------------------------------------------------------------------- +bool RaceVote::hasVotedMinor() const +{ + return m_has_voted_minor; +} +//----------------------------------------------------------------------------- +bool RaceVote::hasVotedTrack(uint8_t track_number) const +{ + return m_tracks_vote[track_number].has_voted_track; +} +//----------------------------------------------------------------------------- +bool RaceVote::hasVotedReversed(uint8_t track_number) const +{ + return m_tracks_vote[track_number].has_voted_reversed; +} +//----------------------------------------------------------------------------- +bool RaceVote::hasVotedLaps(uint8_t track_number) const +{ + return m_tracks_vote[track_number].has_voted_laps; +} + +//----------------------------------------------------------------------------- + +uint8_t RaceVote::getMajorVote() const +{ + return m_major_mode; +} +//----------------------------------------------------------------------------- +uint8_t RaceVote::getRacesCountVote() const +{ + return m_races_count; +} +//----------------------------------------------------------------------------- +uint8_t RaceVote::getMinorVote() const +{ + return m_minor_mode; +} +//----------------------------------------------------------------------------- +std::string RaceVote::getTrackVote(uint8_t track_number) const +{ + return m_tracks_vote[track_number].track_info.track; +} +//----------------------------------------------------------------------------- +bool RaceVote::getReversedVote(uint8_t track_number) const +{ + return m_tracks_vote[track_number].track_info.reversed; +} +//----------------------------------------------------------------------------- +uint8_t RaceVote::getLapsVote(uint8_t track_number) const +{ + return m_tracks_vote[track_number].track_info.laps; +} + +//----------------------------------------------------------------------------- +//--------------------------------- RaceConfig ------------------------------- +//----------------------------------------------------------------------------- + +RaceConfig::RaceConfig() +{ + m_max_players = 0; +} + +//----------------------------------------------------------------------------- + +void RaceConfig::setPlayerCount(uint8_t count) +{ + m_max_players = count; + m_votes.resize(m_max_players); +} + +//----------------------------------------------------------------------------- + +void RaceConfig::setPlayerMajorVote(uint8_t player_id, uint8_t major) +{ + Log::info("RaceConfig", "Player %d voted for major %d", player_id, major); + m_votes[player_id].voteMajor(major); +} + +//----------------------------------------------------------------------------- + +void RaceConfig::setPlayerRaceCountVote(uint8_t player_id, uint8_t count) +{ + Log::info("RaceConfig", "Player %d voted for %d races in GP", player_id, count); + m_votes[player_id].voteRaceCount(count); +} + +//----------------------------------------------------------------------------- + +void RaceConfig::setPlayerMinorVote(uint8_t player_id, uint8_t minor) +{ + Log::info("RaceConfig", "Player %d voted for minor %d", player_id, minor); + m_votes[player_id].voteMinor(minor); +} + +//----------------------------------------------------------------------------- + +void RaceConfig::setPlayerTrackVote(uint8_t player_id, std::string track, uint8_t track_nb) +{ + Log::info("RaceConfig", "Player %d voted for track %s", player_id, track.c_str()); + m_votes[player_id].voteTrack(track, track_nb); +} + +//----------------------------------------------------------------------------- + +void RaceConfig::setPlayerReversedVote(uint8_t player_id, bool reversed, uint8_t track_nb) +{ + if (reversed) + Log::info("RaceConfig", "Player %d voted map %d to be reversed", player_id, track_nb); + else + Log::info("RaceConfig", "Player %d voted map %d NOT to be reversed", player_id, track_nb); + m_votes[player_id].voteReversed(reversed, track_nb); +} + +//----------------------------------------------------------------------------- + +void RaceConfig::setPlayerLapsVote(uint8_t player_id, uint8_t lap_count, uint8_t track_nb) +{ + Log::info("RaceConfig", "Player %d voted map %d to have %d laps", player_id, track_nb, lap_count); + m_votes[player_id].voteLaps(lap_count, track_nb); +} + +//----------------------------------------------------------------------------- + +void RaceConfig::computeRaceMode() +{ + // calculate the race type and number of tracks (in GP mode). + std::map major_histogram; + std::map races_count_histogram; + std::map minor_histogram; + for (unsigned int i = 0; i < m_max_players; i++) + { + // increase the count of votes + if (m_votes[i].hasVotedMajor()) + { + try + { + major_histogram.at(m_votes[i].getMajorVote()) ++; + } + catch (const std::out_of_range&) // doesn't exist in the map + { + major_histogram[m_votes[i].getMajorVote()] = 1; + } + } + else if (m_votes[i].hasVotedRacesCount()) + { + try + { + races_count_histogram.at(m_votes[i].getRacesCountVote()) ++; + } + catch (const std::out_of_range&) // doesn't exist in the map + { + races_count_histogram[m_votes[i].getRacesCountVote()] = 1; + } + } + else if (m_votes[i].hasVotedMinor()) + { + try + { + minor_histogram.at(m_votes[i].getMinorVote()) ++; + } + catch (const std::out_of_range&) // doesn't exist in the map + { + minor_histogram[m_votes[i].getMinorVote()] = 1; + } + } + } + // now we know : + m_major_mode = ((major_histogram.size() > 0) ? getHighestInHistogram(&major_histogram) : 1); + m_races_count = ((minor_histogram.size() > 0) ? getHighestInHistogram(&races_count_histogram) : 1); + m_minor_mode = ((minor_histogram.size() > 0) ? getHighestInHistogram(&minor_histogram) : 0); + + if (m_major_mode == RaceManager::MAJOR_MODE_GRAND_PRIX) + m_tracks.resize(m_races_count); + else + { + m_tracks.resize(1); + m_races_count = 1; + } + + Log::info("RaceConfig", "Major mode will be %d with %d races. Minor is %d", m_major_mode, m_races_count, m_minor_mode); +} +void RaceConfig::computeNextTrack() +{ + for (unsigned int j = 0; j < m_races_count; j++) + { + // first create histograms of the votes + std::map tracks_histogram; + std::map reversed_histogram; + std::map laps_histogram; + for (unsigned int i = 0; i < m_max_players; i++) + { + // increase the count of votes + if (m_votes[i].hasVotedTrack()) + { + try // maps + { + tracks_histogram.at(m_votes[i].getTrackVote()) ++; + } + catch (const std::out_of_range&) // doesn't exist in the map + { + tracks_histogram[m_votes[i].getTrackVote()] = 1; + } + } + else if (m_votes[i].hasVotedReversed()) + { + try // reversed + { + reversed_histogram.at(m_votes[i].getReversedVote()) ++; + } + catch (const std::out_of_range&) // doesn't exist in the map + { + reversed_histogram[m_votes[i].getReversedVote()] = 1; + } + } + else if (m_votes[i].hasVotedLaps()) + { + try // laps + { + laps_histogram.at(m_votes[i].getLapsVote()) ++; + } + catch (const std::out_of_range&) // doesn't exist in the mapt + { + laps_histogram[m_votes[i].getLapsVote()] = 1; + } + } + } + // now find the highest votes + m_tracks[j].track = getHighestInHistogram(&tracks_histogram); + m_tracks[j].reversed = getHighestInHistogram(&reversed_histogram); + m_tracks[j].laps = getHighestInHistogram(&laps_histogram); + if (m_tracks[j].reversed) + Log::info("RaceConfig", "Race %d will be on %s with %d laps and reversed", j, m_tracks[j].track.c_str(), m_tracks[j].laps); + else + Log::info("RaceConfig", "Race %d will be on %s with %d laps", j, m_tracks[j].track.c_str(), m_tracks[j].laps); + } +} + +//----------------------------------------------------------------------------- + +const TrackInfo* RaceConfig::getNextTrackInfo() const +{ + return &m_tracks[0]; +} + +//----------------------------------------------------------------------------- diff --git a/src/network/race_config.hpp b/src/network/race_config.hpp new file mode 100644 index 000000000..64829972a --- /dev/null +++ b/src/network/race_config.hpp @@ -0,0 +1,98 @@ +#ifndef RACE_CONFIG_HPP +#define RACE_CONFIG_HPP + +#include +#include +#include "utils/types.hpp" + +class TrackInfo +{ + public: + TrackInfo() { laps = 0; reversed = false; } + std::string track; + bool reversed; + uint8_t laps; +}; +class TrackVote +{ + public: + TrackVote(); + + void voteTrack(std::string track); + void voteReversed(bool reversed); + void voteLaps(uint8_t laps); + + TrackInfo track_info; + + bool has_voted_track; + bool has_voted_reversed; + bool has_voted_laps; +}; +class RaceVote +{ + public: + RaceVote(); + + void voteMajor(uint8_t major); + void voteRaceCount(uint8_t count); + void voteMinor(uint8_t minor); + void voteTrack(std::string track, uint8_t track_number = 0); + void voteReversed(bool reversed, uint8_t track_number = 0); + void voteLaps(uint8_t laps, uint8_t track_number = 0); + + bool hasVotedMajor() const; + bool hasVotedRacesCount() const; + bool hasVotedMinor() const; + bool hasVotedTrack(uint8_t track_number = 0) const; + bool hasVotedReversed(uint8_t track_number = 0) const; + bool hasVotedLaps(uint8_t track_number = 0) const; + + uint8_t getMajorVote() const; + uint8_t getRacesCountVote() const; + uint8_t getMinorVote() const; + std::string getTrackVote(uint8_t track_number = 0) const; + bool getReversedVote(uint8_t track_number = 0) const; + uint8_t getLapsVote(uint8_t track_number = 0) const; + + private: + uint8_t m_major_mode; + uint8_t m_minor_mode; + uint8_t m_races_count; //!< Stores the number of races that will be in a GP + bool m_has_voted_major; + bool m_has_voted_minor; + bool m_has_voted_races_count; + std::vector m_tracks_vote; +}; + +class RaceConfig +{ + public: + RaceConfig(); + + void setPlayerCount(uint8_t count); + void setPlayerMajorVote(uint8_t player_id, uint8_t major); + void setPlayerRaceCountVote(uint8_t player_id, uint8_t count); + void setPlayerMinorVote(uint8_t player_id, uint8_t minor); + void setPlayerTrackVote(uint8_t player_id, std::string track, uint8_t track_nb = 0); + void setPlayerReversedVote(uint8_t player_id, bool reversed, uint8_t track_nb = 0); + void setPlayerLapsVote(uint8_t player_id, uint8_t lap_count, uint8_t track_nb = 0); + + void computeRaceMode(); + void computeNextTrack(); + + const TrackInfo* getNextTrackInfo() const; + bool getReverse() const; + bool getLapCount() const; + + protected: + std::vector m_tracks; + int m_minor_mode; + int m_major_mode; + unsigned int m_races_count; + + std::vector m_votes; + uint8_t m_max_players; +}; + + +#endif // RACE_CONFIG_HPP diff --git a/src/network/race_info_message.cpp b/src/network/race_info_message.cpp deleted file mode 100644 index 4d2b6aa0b..000000000 --- a/src/network/race_info_message.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 "network/race_info_message.hpp" - -#include "race/grand_prix_manager.hpp" -#include "race/race_manager.hpp" - -RaceInfoMessage::RaceInfoMessage(const std::vector& kart_info) - : Message(Message::MT_RACE_INFO) -{ - const GrandPrixData *cup=NULL; - int len = 2*getCharLength() // major, difficulty - + getIntLength() // minor - which is too big for a char/short! - + getCharLength(); // num karts - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - { - cup = race_manager->getGrandPrix(); - len += getStringLength(cup->getId()); - } - else - { - len += getStringLength(race_manager->getTrackName()); - len += getCharLength(); // num laps - } - len += getCharLength(); // kart_info.size() - for(unsigned int i=0; i& rkl=race_manager->getAIKartList(); - len += getStringVectorLength(rkl); - - allocate(len); - addChar(race_manager->getMajorMode() ); - addInt (race_manager->getMinorMode() ); - addChar(race_manager->getDifficulty() ); - addChar(race_manager->getNumberOfKarts()); - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - addString(cup->getId()); - else - { - addString(race_manager->getTrackName()); - addChar(race_manager->getNumLaps()); - } - - addChar(kart_info.size()); - for(unsigned int i=0; isetMajorMode ( RaceManager::MajorRaceModeType(getChar()) ); - race_manager->setMinorMode ( RaceManager::MinorRaceModeType(getInt()) ); - race_manager->setDifficulty( RaceManager::Difficulty (getChar()) ); - race_manager->setNumKarts ( getChar() ); - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - { - const GrandPrixData *cup = grand_prix_manager->getGrandPrix(getString()); - race_manager->setGrandPrix(*cup); - } - else - { - race_manager->setTrack(getString()); - race_manager->setNumLaps(getChar()); - } - - std::vector kart_info; - kart_info.resize(getChar()); - - for(unsigned int i=0; isetNumPlayers(kart_info.size()); - for(unsigned int i=0; isetPlayerKart(i, kart_info[i]); - } - std::vector rkl=getStringVector(); - race_manager->setAIKartList(rkl); -} // RaceInfoMessage diff --git a/src/network/race_result_ack_message.hpp b/src/network/race_result_ack_message.hpp deleted file mode 100644 index eda990208..000000000 --- a/src/network/race_result_ack_message.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 HEADER_RACE_RESULT_ACK_MESSAGE_HPP -#define HEADER_RACE_RESULT_ACK_MESSAGE_HPP - -#include - -#include "network/message.hpp" - - -/** This message is sent from the clients to the server when the race result - * screen was acknowledged, and then from the server to all clients to - * finish synchronisation. - */ -class RaceResultAckMessage : public Message -{ -private: - char m_menu_selected; -public: - /** Constructor, creates an empty message - */ - RaceResultAckMessage(char menu_choice) : Message(Message::MT_RACE_RESULT_ACK) - { - allocate(getCharLength()); - addChar(menu_choice); - } // RaceResultAckMessage - - // ------------------------------------------------------------------------ - /** Receives the ack message. - * \param pkt Received enet packet. - */ - RaceResultAckMessage(ENetPacket* pkt):Message(pkt, MT_RACE_RESULT_ACK) - { - m_menu_selected = getChar(); - } // RaceResultAckMessage(EnetPacket) - // ------------------------------------------------------------------------ - /** Returns the menu selected on the server after this message is received - * on a client. */ - char getSelectedMenu() const {return m_menu_selected; } - -}; // RaceResultAckMessageMessage -#endif diff --git a/src/network/race_result_message.cpp b/src/network/race_result_message.cpp deleted file mode 100644 index eeaacd656..000000000 --- a/src/network/race_result_message.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 "network/race_result_message.hpp" - -#include "karts/abstract_kart.hpp" -#include "modes/world.hpp" -#include "race/race_manager.hpp" - -/** Creates a message containing the finishing time and rank of each kart. - * This message is serialised so that it can be sent. - */ -RaceResultMessage::RaceResultMessage() : Message(MT_RACE_RESULT) -{ - World *world = World::getWorld(); - const unsigned int num_karts = world->getNumKarts(); - allocate(num_karts * (getFloatLength()+getCharLength())); - for(unsigned int i=0; igetKart(i); - addFloat(kart->getFinishTime()); - addChar(kart->getPosition()); - } // for i in karts -} // RaceResultMessage - -// ---------------------------------------------------------------------------- -/** De-serialises a race result message and sets the appropriate results in - * the kart and the race manager. - * \param pkt The enet message paket. - */ -RaceResultMessage::RaceResultMessage(ENetPacket* pkt) - : Message(pkt, MT_RACE_RESULT) -{ - World *world = World::getWorld(); - const unsigned int num_karts = world->getNumKarts(); - for(unsigned int i=0; igetKart(i); - float time = getFloat(); - char position = getChar(); - kart->setPosition(position); - kart->finishedRace(time); - } -} // RaceResultMessage - diff --git a/src/network/race_state.cpp b/src/network/race_state.cpp deleted file mode 100644 index db5c66c83..000000000 --- a/src/network/race_state.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 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 "network/race_state.hpp" - -#include "items/item_manager.hpp" -#include "items/powerup.hpp" -#include "items/projectile_manager.hpp" -#include "karts/rescue_animation.hpp" -#include "modes/world.hpp" -#include "network/network_manager.hpp" -#include "physics/physics.hpp" - -RaceState *race_state=NULL; - -// ---------------------------------------------------------------------------- -void RaceState::serialise() -{ - // First compute the overall size needed - // ===================================== - int len = 0; - - // 1. Add all kart information - // --------------------------- - unsigned int num_karts = World::getWorld()->getCurrentNumKarts(); - KartControl c; - // Send the number of karts and for each kart the compressed - // control structure, xyz,hpr, and speed (which is necessary to - // display the speed, and e.g. to determine when a parachute is detached) - len += 1 + num_karts*(KartControl::getLength() - + getVec3Length()+getQuaternionLength() - + getFloatLength()) ; - - // 2. Add information about collected items - // --------------------------------------- - len += 1 + m_item_info.size()* ItemInfo::getLength(); - - // 3. Add rocket positions - // ----------------------- - len += 2 + m_flyable_info.size()*FlyableInfo::getLength(); - - // 4. Add collisions - // ================= - len += 1 + m_collision_info.size()*getCharLength(); - - // Now add the data - // ================ - allocate(len); - - // 1. Kart positions - // ----------------- - addChar(num_karts); - World *world = World::getWorld(); - for(unsigned int i=0; igetKart(i); - m_kart_controls[i].serialise(this); - addVec3(kart->getXYZ()); - addQuaternion(kart->getRotation()); - addFloat(kart->getSpeed()); - } // for i - - // 2. Collected items - // ----------------- - addChar(m_item_info.size()); - for(unsigned int i=0; igetKart(i); - // Firing needs to be done from here to guarantee that any potential - // new rockets are created before the update for the rockets is handled - if(kc.m_fire) - kart->getPowerup()->use(); - kart->setXYZ(xyz); - kart->setRotation(q); - kart->setSpeed(getFloat()); - } // for i - - // 2. Collected Items - // ----------------- - unsigned short num_items=getChar(); - for(unsigned int i=0; igetKart(hi.m_kart_id)); - else - { - Item *item = ItemManager::get()->getItem(hi.m_item_id); - ItemManager::get()->collectedItem(item, - world->getKart(hi.m_kart_id), - hi.m_add_info); - } - } - - // 3. Projectiles - // -------------- - unsigned short num_flyables = getShort(); - m_flyable_info.clear(); - m_flyable_info.resize(num_flyables); - for(unsigned short i=0; igetKart(kart_id1)->crashed(NULL, normal); - } - else - { - // FIXME: KartKartCollision now takes information about the - // collision points. This either needs to be added as the third - // parameter, or perhaps the outcome of the collision (the - // impulse) could be added. - world->getPhysics()->KartKartCollision( - world->getKart(kart_id1), Vec3(0,0,0), - world->getKart(kart_id2), Vec3(0,0,0)); - } - } // for(i=0; i - -#include "items/flyable.hpp" -#include "items/item.hpp" -#include "karts/abstract_kart.hpp" -#include "karts/controller/kart_control.hpp" -#include "modes/world.hpp" -#include "network/flyable_info.hpp" -#include "network/item_info.hpp" -#include "network/message.hpp" -#include "utils/aligned_array.hpp" - -/** This class stores the state information of a (single) race, e.g. the - position and orientation of karts, collisions that have happened etc. - It is used for the network version to update the clients with the - 'official' state information from the server. - */ -class RaceState : public Message -{ -private: - - /** Updates about collected items. */ - std::vector m_item_info; - /** Updates about existing flyables. */ - AlignedArray m_flyable_info; - /** Stores the controls of each kart at the beginning of its update(). */ - std::vector m_kart_controls; - /** Collision information. This vector stores information about which - * kart collided with which kart or track (kartid=-1) */ - std::vector m_collision_info; - - public: - /** Initialise the global race state. */ - RaceState() : Message(MT_RACE_STATE) - { - m_kart_controls.resize(World::getWorld()->getNumKarts()); - } // RaceState() - // -------------------------------------------------------------------- - void itemCollected(int kartid, int item_id, char add_info=-1) - { - m_item_info.push_back(ItemInfo(kartid, item_id, add_info)); - } // itemCollected - // -------------------------------------------------------------------- - /** Collects information about collision in which at least one kart was - * involved. Other collision (e.g. projectiles, moving physics) are - * not needed on the client, so it's not stored at all. If a kart - * track collision happens, the second kart id is -1 (necessary to - * play back sound effects). A simple int vector is used to store the - * pair of collision, so the first collision is using the index 0 and - * 1; the second one 2 and 3 etc. - * \param kartId1 World id of the kart involved in the collision. - * \param kartId2 World id of the 2nd kart involved in the collision, - * or -1 if it's the track (which is the default). - */ - void addCollision(signed char kartId1, signed char kartId2=-1) - { - m_collision_info.push_back(kartId1); - m_collision_info.push_back(kartId2); - } // addCollision - // -------------------------------------------------------------------- - void setNumFlyables(int n) { m_flyable_info.resize(n); } - // -------------------------------------------------------------------- - void setFlyableInfo(int n, const FlyableInfo& fi) - { - m_flyable_info[n] = fi; - } - // -------------------------------------------------------------------- - /** Stores the current kart control (at the time kart->update() is - * called. This allows modifications of kart->m_control during the - * update (e.g. see in kart::update() how firing is handled). - */ - void storeKartControls(const AbstractKart& kart) - { - m_kart_controls[kart.getWorldKartId()] = kart.getControls(); - } // storeKartControls - // -------------------------------------------------------------------- - void serialise(); - void receive(ENetPacket *pkt); - void clear(); // Removes all currently stored information - unsigned int getNumFlyables() const {return m_flyable_info.size(); } - const FlyableInfo - &getFlyable(unsigned int i) const {return m_flyable_info[i];} - }; // RaceState - -extern RaceState *race_state; - -#endif - diff --git a/src/network/remote_kart_info.hpp b/src/network/remote_kart_info.hpp index cde5cda72..a1ad45545 100644 --- a/src/network/remote_kart_info.hpp +++ b/src/network/remote_kart_info.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,6 +16,9 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/*! \file remote_kart_info.hpp + */ + #ifndef HEADER_REMOTE_KART_INFO_HPP #define HEADER_REMOTE_KART_INFO_HPP @@ -38,12 +41,15 @@ class RemoteKartInfo int m_global_player_id; int m_host_id; SoccerTeam m_soccer_team; + bool m_network_player; public: RemoteKartInfo(int player_id, const std::string& kart_name, - const irr::core::stringw& user_name, int host_id) + const irr::core::stringw& user_name, int host_id, bool network) : m_kart_name(kart_name), m_user_name(user_name), - m_local_player_id(player_id), m_host_id(host_id), m_soccer_team(SOCCER_TEAM_NONE) + m_local_player_id(player_id), m_host_id(host_id), + m_soccer_team(SOCCER_TEAM_NONE), m_network_player(network) + {}; RemoteKartInfo(const std::string& kart_name) {m_kart_name=kart_name; m_user_name=""; @@ -56,10 +62,12 @@ public: void setLocalPlayerId(int id) { m_local_player_id = id; } void setGlobalPlayerId(int id) { m_global_player_id = id; } const void setSoccerTeam(SoccerTeam team) { m_soccer_team = team; } + void setNetworkPlayer(bool value) { m_network_player = value; } int getHostId() const { return m_host_id; } int getLocalPlayerId() const { return m_local_player_id; } int getGlobalPlayerId() const { return m_global_player_id; } + bool isNetworkPlayer() const { return m_network_player; } const std::string& getKartName() const { return m_kart_name; } const irr::core::stringw& getPlayerName() const { return m_user_name; } const SoccerTeam getSoccerTeam() const {return m_soccer_team; } diff --git a/src/network/server_network_manager.cpp b/src/network/server_network_manager.cpp new file mode 100644 index 000000000..c6d33aaf0 --- /dev/null +++ b/src/network/server_network_manager.cpp @@ -0,0 +1,132 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/server_network_manager.hpp" + +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/protocols/stop_server.hpp" +#include "network/protocols/server_lobby_room_protocol.hpp" + +#include "main_loop.hpp" +#include "utils/log.hpp" + +#include +#include +#include +#include +#include + +void* waitInput2(void* data) +{ + std::string str = ""; + bool stop = false; + while(!stop) + { + getline(std::cin, str); + if (str == "quit") + { + stop = true; + } + else if (str == "kickall") + { + ServerNetworkManager::getInstance()->kickAllPlayers(); + } + else if (str == "start") + { + ServerLobbyRoomProtocol* protocol = static_cast(ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + assert(protocol); + protocol->startGame(); + } + else if (str == "selection") + { + ServerLobbyRoomProtocol* protocol = static_cast(ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + assert(protocol); + protocol->startSelection(); + } + else if (str == "compute_race") + { + GameSetup* setup = NetworkManager::getInstance()->getGameSetup(); + setup->getRaceConfig()->computeRaceMode(); + } + else if (str == "compute_track") + { + GameSetup* setup = NetworkManager::getInstance()->getGameSetup(); + setup->getRaceConfig()->computeNextTrack(); + } + } + + uint32_t id = ProtocolManager::getInstance()->requestStart(new StopServer()); + while(ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED) + { + StkTime::sleep(1); + } + + main_loop->abort(); + + return NULL; +} + +ServerNetworkManager::ServerNetworkManager() +{ + m_localhost = NULL; + m_thread_keyboard = NULL; +} + +ServerNetworkManager::~ServerNetworkManager() +{ + if (m_thread_keyboard) + pthread_cancel(*m_thread_keyboard);//, SIGKILL); +} + +void ServerNetworkManager::run() +{ + if (enet_initialize() != 0) + { + Log::error("ServerNetworkManager", "Could not initialize enet."); + return; + } + m_localhost = new STKHost(); + m_localhost->setupServer(STKHost::HOST_ANY, 7321, 16, 2, 0, 0); + m_localhost->startListening(); + + Log::info("ServerNetworkManager", "Host initialized."); + + // listen keyboard console input + m_thread_keyboard = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_thread_keyboard, NULL, waitInput2, NULL); + + NetworkManager::run(); + Log::info("ServerNetworkManager", "Ready."); +} + +void ServerNetworkManager::kickAllPlayers() +{ + for (unsigned int i = 0; i < m_peers.size(); i++) + { + m_peers[i]->disconnect(); + } +} + +void ServerNetworkManager::sendPacket(const NetworkString& data, bool reliable) +{ + m_localhost->broadcastPacket(data, reliable); +} diff --git a/src/network/server_network_manager.hpp b/src/network/server_network_manager.hpp new file mode 100644 index 000000000..1f85e879a --- /dev/null +++ b/src/network/server_network_manager.hpp @@ -0,0 +1,57 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file server_network_manager.hpp + */ + +#ifndef SERVER_NETWORK_MANAGER_HPP +#define SERVER_NETWORK_MANAGER_HPP + +#include "network/network_manager.hpp" + + +class ServerNetworkManager : public NetworkManager +{ + friend class Singleton; + public: + static ServerNetworkManager* getInstance() + { + return Singleton::getInstance(); + } + + virtual void run(); + + void setMaxPlayers(uint8_t count) { m_max_players = count; } + uint8_t getMaxPlayers() {return m_max_players;} + + void kickAllPlayers(); + + virtual void sendPacket(const NetworkString& data, bool reliable = true); + + virtual bool isServer() { return true; } + + protected: + ServerNetworkManager(); + virtual ~ServerNetworkManager(); + + pthread_t* m_thread_keyboard; + uint8_t m_max_players; + +}; + +#endif // SERVER_NETWORK_MANAGER_HPP diff --git a/src/network/singleton.hpp b/src/network/singleton.hpp new file mode 100644 index 000000000..793c74584 --- /dev/null +++ b/src/network/singleton.hpp @@ -0,0 +1,84 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file singleton.hpp + */ + +#ifndef SINGLETON_HPP +#define SINGLETON_HPP + +#include "utils/log.hpp" + +/*! \class ProtocolManager + * \brief Manages the protocols at runtime. + * This has been designed to allow multi-inheritance. This is advised to + * re-declare getInstance, but whithout templates parameters in the inheriting + * classes. + */ +template +class Singleton +{ + protected: + /*! \brief Constructor */ + Singleton () { m_singleton = NULL; } + /*! \brief Destructor */ + virtual ~Singleton () + { + Log::info("Singleton", "Destroyed singleton."); + } + + public: + /*! \brief Used to get the instance, after a dynamic cast. + * This is important when making a double-inheritance of this class. + * For example, if A is a singleton inherited by B, you can call + * B::getInstance() to have the instance returned as a A*. + * If the cast fails, a log message will notify it. + */ + template + static S *getInstance () + { + if (m_singleton == NULL) + m_singleton = new S; + + S* result = (dynamic_cast (m_singleton)); + if (result == NULL) + Log::debug("Singleton", "THE SINGLETON HAS NOT BEEN REALOCATED, IT IS NOT OF THE REQUESTED TYPE."); + return result; + } + /*! \brief Used to get the instance. */ + static T *getInstance() + { + return m_singleton; + } + + /*! \brief Used to kill the singleton, if needed. */ + static void kill () + { + if (m_singleton) + { + delete m_singleton; + } + } + + private: + static T *m_singleton; +}; + +template T *Singleton::m_singleton = NULL; + +#endif // SINGLETON_HPP diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp new file mode 100644 index 000000000..2ad40aeac --- /dev/null +++ b/src/network/stk_host.cpp @@ -0,0 +1,369 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/stk_host.hpp" + +#include "config/user_config.hpp" +#include "network/network_manager.hpp" +#include "utils/log.hpp" +#include "utils/time.hpp" + +#include +#ifdef WIN32 +# include "Ws2tcpip.h" +# define inet_ntop InetNtop +#else +# include +# include +#endif +#include +#include + +FILE* STKHost::m_log_file = NULL; +pthread_mutex_t STKHost::m_log_mutex; + +void STKHost::logPacket(const NetworkString ns, bool incoming) +{ + if (m_log_file == NULL) + return; + pthread_mutex_lock(&m_log_mutex); + if (incoming) + fprintf(m_log_file, "[%d\t] <-- ", (int)(StkTime::getRealTime())); + else + fprintf(m_log_file, "[%d\t] --> ", (int)(StkTime::getRealTime())); + for (int i = 0; i < ns.size(); i++) + { + fprintf(m_log_file, "%d.", ns[i]); + } + fprintf(m_log_file, "\n"); + pthread_mutex_unlock(&m_log_mutex); +} + +// ---------------------------------------------------------------------------- + +void* STKHost::receive_data(void* self) +{ + ENetEvent event; + STKHost* myself = (STKHost*)(self); + ENetHost* host = myself->m_host; + while (!myself->mustStopListening()) + { + while (enet_host_service(host, &event, 20) != 0) { + Event* evt = new Event(&event); + if (evt->type == EVENT_TYPE_MESSAGE) + logPacket(evt->data(), true); + if (event.type != ENET_EVENT_TYPE_NONE) + NetworkManager::getInstance()->notifyEvent(evt); + delete evt; + } + } + myself->m_listening = false; + delete myself->m_listening_thread; + myself->m_listening_thread = NULL; + Log::info("STKHost", "Listening has been stopped"); + return NULL; +} + +// ---------------------------------------------------------------------------- + +STKHost::STKHost() +{ + m_host = NULL; + m_listening_thread = NULL; + m_log_file = NULL; + pthread_mutex_init(&m_exit_mutex, NULL); + pthread_mutex_init(&m_log_mutex, NULL); + if (UserConfigParams::m_packets_log_filename.toString() != "") + m_log_file = fopen(UserConfigParams::m_packets_log_filename.c_str(), "w+"); + if (!m_log_file) + Log::warn("STKHost", "Network packets won't be logged: no file."); +} + +// ---------------------------------------------------------------------------- + +STKHost::~STKHost() +{ + stopListening(); + if (m_log_file) + { + fclose(m_log_file); + Log::warn("STKHost", "Packet logging file has been closed."); + } + if (m_host) + { + enet_host_destroy(m_host); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::setupServer(uint32_t address, uint16_t port, int peer_count, + int channel_limit, uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) +{ + ENetAddress* addr = (ENetAddress*)(malloc(sizeof(ENetAddress))); + addr->host = address; + addr->port = port; + +#ifdef WIN32/* + addr->host = 0; + addr->host += ((unsigned int)(192)<<0); // 192.168.0.11 + addr->host += ((unsigned int)(168)<<8); // 192.168.0.11 + addr->host += ((unsigned int)(11)<<24); // 192.168.0.11*/ +#endif + + m_host = enet_host_create(addr, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); + if (m_host == NULL) + { + Log::error("STKHost", "An error occurred while trying to create an ENet" + " server host."); + exit (EXIT_FAILURE); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) +{ + m_host = enet_host_create(NULL, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); + if (m_host == NULL) + { + Log::error("STKHost", "An error occurred while trying to create an ENet" + " client host."); + exit (EXIT_FAILURE); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::startListening() +{ + pthread_mutex_lock(&m_exit_mutex); // will let the update function start + m_listening_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_listening_thread, NULL, &STKHost::receive_data, this); + m_listening = true; +} + +// ---------------------------------------------------------------------------- + +void STKHost::stopListening() +{ + if(m_listening_thread) + { + pthread_mutex_unlock(&m_exit_mutex); // will stop the update function on its next update + pthread_join(*m_listening_thread, NULL); // wait the thread to end + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::sendRawPacket(uint8_t* data, int length, TransportAddress dst) +{ + struct sockaddr_in to; + int to_len = sizeof(to); + memset(&to,0,to_len); + + to.sin_family = AF_INET; + to.sin_port = htons(dst.port); + to.sin_addr.s_addr = htonl(dst.ip); + + sendto(m_host->socket, (char*)data, length, 0,(sockaddr*)&to, to_len); + Log::verbose("STKHost", "Raw packet sent to %i.%i.%i.%i:%u", ((dst.ip>>24)&0xff) + , ((dst.ip>>16)&0xff), ((dst.ip>>8)&0xff), ((dst.ip>>0)&0xff), dst.port); + STKHost::logPacket(NetworkString(std::string((char*)(data), length)), false); +} + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket() +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + int len = recv(m_host->socket,(char*)buffer,2048, 0); + int i = 0; + // wait to receive the message because enet sockets are non-blocking + while(len < 0) + { + i++; + len = recv(m_host->socket,(char*)buffer,2048, 0); + StkTime::sleep(1); + } + STKHost::logPacket(NetworkString(std::string((char*)(buffer), len)), true); + return buffer; +} + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket(TransportAddress* sender) +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + socklen_t from_len; + struct sockaddr_in addr; + + from_len = sizeof(addr); + int len = recvfrom(m_host->socket, (char*)buffer, 2048, 0, (struct sockaddr*)(&addr), &from_len); + + int i = 0; + // wait to receive the message because enet sockets are non-blocking + while(len == -1) // nothing received + { + i++; + len = recvfrom(m_host->socket, (char*)buffer, 2048, 0, (struct sockaddr*)(&addr), &from_len); + StkTime::sleep(1); // wait 1 millisecond between two checks + } + if (len == SOCKET_ERROR) + { + Log::error("STKHost", "Problem with the socket. Please contact the dev team."); + } + // we received the data + sender->ip = ntohl((uint32_t)(addr.sin_addr.s_addr)); + sender->port = ntohs(addr.sin_port); + + if (addr.sin_family == AF_INET) + { + char s[20]; + inet_ntop(AF_INET, &(addr.sin_addr), s, 20); + Log::info("STKHost", "IPv4 Address of the sender was %s", s); + } + STKHost::logPacket(NetworkString(std::string((char*)(buffer), len)), true); + return buffer; +} + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket(TransportAddress sender, int max_tries) +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + socklen_t from_len; + struct sockaddr_in addr; + + from_len = sizeof(addr); + int len = recvfrom(m_host->socket, (char*)buffer, 2048, 0, (struct sockaddr*)(&addr), &from_len); + + int i = 0; + // wait to receive the message because enet sockets are non-blocking + while(len < 0 || addr.sin_addr.s_addr == sender.ip) + { + i++; + if (len>=0) + { + Log::info("STKHost", "Message received but the ip address didn't match the expected one."); + } + len = recvfrom(m_host->socket, (char*)buffer, 2048, 0, (struct sockaddr*)(&addr), &from_len); + uint32_t addr1 = addr.sin_addr.s_addr; + uint32_t addr2 = sender.ip; + uint32_t addr3 = ntohl(addr1); + uint32_t addr4 = ntohl(addr2); + StkTime::sleep(1); // wait 1 millisecond between two checks + if (i >= max_tries && max_tries != -1) + { + Log::verbose("STKHost", "No answer from the server on %u.%u.%u.%u:%u", (m_host->address.host&0xff), + (m_host->address.host>>8&0xff), + (m_host->address.host>>16&0xff), + (m_host->address.host>>24&0xff), + (m_host->address.port)); + return NULL; + } + } + if (addr.sin_family == AF_INET) + { + char s[20]; + inet_ntop(AF_INET, &(addr.sin_addr), s, 20); + Log::info("STKHost", "IPv4 Address of the sender was %s", s); + } + STKHost::logPacket(NetworkString(std::string((char*)(buffer), len)), true); + return buffer; +} + +// ---------------------------------------------------------------------------- + +void STKHost::broadcastPacket(const NetworkString& data, bool reliable) +{ + ENetPacket* packet = enet_packet_create(data.c_str(), data.size()+1, + (reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED)); + enet_host_broadcast(m_host, 0, packet); + STKHost::logPacket(data, false); +} + +// ---------------------------------------------------------------------------- + +bool STKHost::peerExists(TransportAddress peer) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == ntohl(peer.ip) && + m_host->peers[i].address.port == peer.port) + { + return true; + } + } + return false; +} + +// ---------------------------------------------------------------------------- + +bool STKHost::isConnectedTo(TransportAddress peer) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == ntohl(peer.ip) && + m_host->peers[i].address.port == peer.port && + m_host->peers[i].state == ENET_PEER_STATE_CONNECTED) + { + return true; + } + } + return false; +} + +// --------------------------------------------------------------------------- + +int STKHost::mustStopListening() +{ + switch(pthread_mutex_trylock(&m_exit_mutex)) { + case 0: /* if we got the lock, unlock and return 1 (true) */ + pthread_mutex_unlock(&m_exit_mutex); + return 1; + case EBUSY: /* return 0 (false) if the mutex was locked */ + return 0; + } + return 1; +} + +uint16_t STKHost::getPort() const +{ + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + if (getsockname(m_host->socket, (struct sockaddr *)&sin, &len) == -1) + Log::error("STKHost", "Error while using getsockname()."); + else + return ntohs(sin.sin_port); + return 0; +} diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp new file mode 100644 index 000000000..a9951000c --- /dev/null +++ b/src/network/stk_host.hpp @@ -0,0 +1,173 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file stk_host.hpp + * \brief Defines an interface to use network low-level functions easily. + */ +#ifndef STK_HOST_HPP +#define STK_HOST_HPP + +#include "network/types.hpp" + +#include "network/network_string.hpp" + +// enet.h includes win32.h, which without lean_and_mean includes +// winspool.h, which defines MAX_PRIORITY as a macro, which then +// results in http_manager.hpp not being compilable. +#define WIN32_LEAN_AND_MEAN +#include + +#include + +/*! \class STKHost + * \brief Represents the local host. + * This host is either a server host or a client host. A client host is in + * charge of connecting to a server. A server opens a socket for incoming + * connections. + * By default, this host will use ENet to exchange packets. It also defines an + * interface for ENet use. Nevertheless, this class can be used to send and/or + * receive packets whithout ENet adding its headers. + * This class is used by the Network Manager to send packets. + */ +class STKHost +{ + friend class STKPeer; // allow direct enet modifications in implementations + public: + /*! \enum HOST_TYPE + * \brief Defines three host types for the server. + * These values tells the host where he will accept connections from. + */ + enum HOST_TYPE + { + HOST_ANY = 0, //!< Any host. + HOST_BROADCAST = 0xFFFFFFFF, //!< Defines the broadcast address. + PORT_ANY = 0 //!< Any port. + }; + + /*! \brief Constructor */ + STKHost(); + /*! \brief Destructor */ + virtual ~STKHost(); + + /*! \brief Log packets into a file + * \param ns : The data in the packet + * \param incoming : True if the packet comes from a peer. + * False if it's sent to a peer. + */ + static void logPacket(const NetworkString ns, bool incoming); + + /*! \brief Thread function checking if data is received. + * This function tries to get data from network low-level functions as + * often as possible. When something is received, it generates an + * event and passes it to the Network Manager. + * \param self : used to pass the ENet host to the function. + */ + static void* receive_data(void* self); + + /*! \brief Setups the host as a server. + * \param address : The IPv4 address of incoming connections. + * \param port : The port on which the server listens. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupServer(uint32_t address, uint16_t port, + int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + /*! \brief Setups the host as a client. + * In fact there is only one peer connected to this host. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + + /*! \brief Starts the listening of events from ENet. + * Starts a thread that updates it as often as possible. + */ + void startListening(); + /*! \brief Stops the listening of events from ENet. + * Stops the thread that was receiving events. + */ + void stopListening(); + + /*! \brief Sends a packet whithout ENet adding its headers. + * This function is used in particular to achieve the STUN protocol. + * \param data : Data to send. + * \param length : Length of the sent data. + * \param dst : Destination of the packet. + */ + void sendRawPacket(uint8_t* data, int length, + TransportAddress dst); + /*! \brief Receives a packet directly from the network interface. + * Receive a packet whithout ENet processing it. + * \return A string containing the data of the received packet. + */ + uint8_t* receiveRawPacket(); + uint8_t* receiveRawPacket(TransportAddress* sender); + /*! \brief Receives a packet directly from the network interface and + * filter its address. + * Receive a packet whithout ENet processing it. Checks that the + * sender of the packet is the one that corresponds to the sender + * parameter. Does not check the port right now. + * \param sender : Transport address of the original sender of the + * wanted packet. + * \param max_tries : Number of times we try to read data from the + * socket. This is aproximately the time we wait in milliseconds. + * -1 means eternal tries. + * \return A string containing the data of the received packet + * matching the sender's ip address. + */ + uint8_t* receiveRawPacket(TransportAddress sender, int max_tries = -1); + /*! \brief Broadcasts a packet to all peers. + * \param data : Data to send. + */ + void broadcastPacket(const NetworkString& data, bool reliable = true); + + /*! \brief Tells if a peer is known. + * \return True if the peer is known, false elseway. + */ + bool peerExists(TransportAddress peer_address); + /*! \brief Tells if a peer is known and connected. + * \return True if the peer is known and connected, false elseway. + */ + bool isConnectedTo(TransportAddress peer_address); + + /*! \brief Returns true when the thread should stop listening. */ + int mustStopListening(); + /*! \brief Returns true when the thread has stopped listening. */ + bool hasStoppedListening() const { return m_listening; } + + uint32_t getAddress() const { return m_host->address.host; } + uint16_t getPort() const; + protected: + ENetHost* m_host; //!< ENet host interfacing sockets. + pthread_t* m_listening_thread; //!< Thread listening network events. + pthread_mutex_t m_exit_mutex; //!< Mutex to kill properly the thread + bool m_listening; + static FILE* m_log_file; //!< Where to log packets + static pthread_mutex_t m_log_mutex; //!< To write in the log only once at a time + +}; + +#endif // STK_HOST_HPP diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp new file mode 100644 index 000000000..a68a647a9 --- /dev/null +++ b/src/network/stk_peer.cpp @@ -0,0 +1,148 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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 "network/stk_peer.hpp" +#include "network/network_manager.hpp" +#include "utils/log.hpp" + +#include + +STKPeer::STKPeer() +{ + m_peer = NULL; + m_player_profile = new NetworkPlayerProfile*; + *m_player_profile = NULL; + m_client_server_token = new uint32_t; + *m_client_server_token = 0; + m_token_set = new bool; + *m_token_set = false; +} + +//----------------------------------------------------------------------------- + +STKPeer::STKPeer(const STKPeer& peer) +{ + m_peer = peer.m_peer; + m_player_profile = peer.m_player_profile; + m_client_server_token = peer.m_client_server_token; + m_token_set = peer.m_token_set; +} + +//----------------------------------------------------------------------------- + +STKPeer::~STKPeer() +{ + if (m_peer) + m_peer = NULL; + if (m_player_profile) + delete m_player_profile; + m_player_profile = NULL; +} + +//----------------------------------------------------------------------------- + +bool STKPeer::connectToHost(STKHost* localhost, TransportAddress host, + uint32_t channel_count, uint32_t data) +{ + ENetAddress address; + address.host = + ((host.ip & 0xff000000) >> 24) + + ((host.ip & 0x00ff0000) >> 8) + + ((host.ip & 0x0000ff00) << 8) + + ((host.ip & 0x000000ff) << 24); // because ENet wants little endian + address.port = host.port; + + ENetPeer* peer = enet_host_connect(localhost->m_host, &address, 2, 0); + if (peer == NULL) + { + Log::error("STKPeer", "Could not try to connect to server.\n"); + return false; + } + Log::verbose("STKPeer", "Connecting to %i.%i.%i.%i:%i.\nENetPeer address " + "is %ld", (peer->address.host>>0)&0xff, + (peer->address.host>>8)&0xff,(peer->address.host>>16)&0xff, + (peer->address.host>>24)&0xff,peer->address.port, (long int)(peer)); + return true; +} + +//----------------------------------------------------------------------------- + +void STKPeer::disconnect() +{ + enet_peer_disconnect(m_peer, 0); +} + +//----------------------------------------------------------------------------- + +void STKPeer::sendPacket(NetworkString const& data, bool reliable) +{ + Log::verbose("STKPeer", "sending packet of size %d to %i.%i.%i.%i:%i", + data.size(), (m_peer->address.host>>0)&0xff, + (m_peer->address.host>>8)&0xff,(m_peer->address.host>>16)&0xff, + (m_peer->address.host>>24)&0xff,m_peer->address.port); + ENetPacket* packet = enet_packet_create(data.c_str(), data.size()+1, + (reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED)); + /* to debug the packet output + printf("STKPeer: "); + for (unsigned int i = 0; i < data.size(); i++) + { + printf("%d ", (uint8_t)(data[i])); + } + printf("\n"); + */ + enet_peer_send(m_peer, 0, packet); +} + +//----------------------------------------------------------------------------- + +uint32_t STKPeer::getAddress() const +{ + return ntohl(m_peer->address.host); +} + +//----------------------------------------------------------------------------- + +uint16_t STKPeer::getPort() const +{ + return m_peer->address.port; +} + +//----------------------------------------------------------------------------- + +bool STKPeer::isConnected() const +{ + Log::info("STKPeer", "The peer state is %i\n", m_peer->state); + return (m_peer->state == ENET_PEER_STATE_CONNECTED); +} + +//----------------------------------------------------------------------------- + +bool STKPeer::exists() const +{ + return (m_peer != NULL); // assert that the peer exists +} + +//----------------------------------------------------------------------------- + +bool STKPeer::isSamePeer(const STKPeer* peer) const +{ + return peer->m_peer==m_peer; +} + +//----------------------------------------------------------------------------- + diff --git a/src/network/stk_peer.hpp b/src/network/stk_peer.hpp new file mode 100644 index 000000000..4603da143 --- /dev/null +++ b/src/network/stk_peer.hpp @@ -0,0 +1,69 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file stk_peer.hpp + * \brief Defines functions to easily manipulate 8-bit network destinated strings. + */ + +#ifndef STK_PEER_HPP +#define STK_PEER_HPP + +#include "network/stk_host.hpp" +#include "network/network_string.hpp" +#include "network/game_setup.hpp" +#include + +/*! \class STKPeer + * \brief Represents a peer. + * This class is used to interface the ENetPeer structure. + */ +class STKPeer +{ + friend class Event; + public: + STKPeer(); + STKPeer(const STKPeer& peer); + virtual ~STKPeer(); + + virtual void sendPacket(const NetworkString& data, bool reliable = true); + static bool connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data); + void disconnect(); + + void setClientServerToken(const uint32_t& token) { *m_client_server_token = token; *m_token_set = true; } + void unsetClientServerToken() { *m_token_set = false; } + void setPlayerProfile(NetworkPlayerProfile* profile) { *m_player_profile = profile; } + void setPlayerProfilePtr(NetworkPlayerProfile** profile) { m_player_profile = profile; } + + bool isConnected() const; + bool exists() const; + uint32_t getAddress() const; + uint16_t getPort() const; + NetworkPlayerProfile* getPlayerProfile() { return (m_player_profile)?(*m_player_profile):NULL; } + uint32_t getClientServerToken() const { return *m_client_server_token; } + bool isClientServerTokenSet() const { return *m_token_set; } + + bool isSamePeer(const STKPeer* peer) const; + + protected: + ENetPeer* m_peer; + NetworkPlayerProfile** m_player_profile; + uint32_t *m_client_server_token; + bool *m_token_set; +}; + +#endif // STK_PEER_HPP diff --git a/src/network/types.cpp b/src/network/types.cpp new file mode 100644 index 000000000..27a5d4fea --- /dev/null +++ b/src/network/types.cpp @@ -0,0 +1 @@ +#include "network/types.hpp" diff --git a/src/network/types.hpp b/src/network/types.hpp new file mode 100644 index 000000000..ad594d37e --- /dev/null +++ b/src/network/types.hpp @@ -0,0 +1,79 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// 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. + +/*! \file types.hpp + * \brief Declares the general types that are used by the network. + */ +#ifndef TYPES_HPP +#define TYPES_HPP + +#include "utils/types.hpp" + +#include + +/*! functions to write easily addresses in logs. */ +#define ADDRESS_FORMAT "%d.%d.%d.%d:%d" +#define ADDRESS_ARGS(ip,port) ((ip>>24)&0xff),((ip>>16)&0xff),((ip>>8)&0xff),((ip>>0)&0xff),port + +/*! \class CallbackObject + * \brief Class that must be inherited to pass objects to protocols. + */ +class CallbackObject +{ + public: + CallbackObject() {} + ~CallbackObject() {} + +}; + +/*! \class TransportAddress + * \brief Describes a transport-layer address. + * For IP networks, a transport address is the couple ip:port. + */ +class TransportAddress : public CallbackObject +{ + public: + TransportAddress(uint32_t p_ip = 0, uint16_t p_port = 0) + { ip = p_ip; port = p_port; } + ~TransportAddress() {} + + bool operator==(const TransportAddress& other) const + { return other.ip == ip && other.port == port; } + + bool operator!=(const TransportAddress& other) const + { return other.ip != ip || other.port != port; } + + uint32_t ip; //!< The IPv4 address + uint16_t port; //!< The port number +}; + +/*! \class PlayerLogin + * \brief Contains the information needed to authenticate a user. + */ +class PlayerLogin : public CallbackObject +{ + public: + PlayerLogin() {} + ~PlayerLogin() { username.clear(); password.clear(); } + + std::string username; //!< Username of the player + std::string password; //!< Password of the player +}; + + +#endif // TYPES_HPP diff --git a/src/online/current_user.cpp b/src/online/current_user.cpp new file mode 100644 index 000000000..11108ee7c --- /dev/null +++ b/src/online/current_user.cpp @@ -0,0 +1,766 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "online/current_user.hpp" + +#include "achievements/achievements_manager.hpp" +#include "addons/addons_manager.hpp" +#include "config/user_config.hpp" +#include "online/servers_manager.hpp" +#include "online/profile_manager.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" +#include "addons/addon.hpp" +#include "guiengine/dialog_queue.hpp" +#include "states_screens/dialogs/change_password_dialog.hpp" +#include "states_screens/dialogs/login_dialog.hpp" +#include "states_screens/dialogs/user_info_dialog.hpp" +#include "states_screens/dialogs/notification_dialog.hpp" +#include "states_screens/online_profile_friends.hpp" + +#include +#include +#include +#include + +using namespace Online; + +namespace Online{ + static CurrentUser* current_user_singleton(NULL); + + CurrentUser* CurrentUser::get() + { + if (current_user_singleton == NULL) + current_user_singleton = new CurrentUser(); + return current_user_singleton; + } + + void CurrentUser::deallocate() + { + delete current_user_singleton; + current_user_singleton = NULL; + } // deallocate + + // ============================================================================ + CurrentUser::CurrentUser() + { + m_state = US_SIGNED_OUT; + m_token = ""; + m_save_session = false; + m_profile = NULL; + } + + // ============================================================================ + const XMLRequest * CurrentUser::requestRecovery( const irr::core::stringw &username, + const irr::core::stringw &email) + { + assert(m_state == US_SIGNED_OUT || m_state == US_GUEST); + XMLRequest * request = new XMLRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("recovery")); + request->setParameter("username", username); + request->setParameter("email", email); + HTTPManager::get()->addRequest(request); + return request; + } + + // ============================================================================ + const XMLRequest * CurrentUser::requestSignUp( const irr::core::stringw &username, + const irr::core::stringw &password, + const irr::core::stringw &password_confirm, + const irr::core::stringw &email, + bool terms) + { + assert(m_state == US_SIGNED_OUT || m_state == US_GUEST); + XMLRequest * request = new XMLRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("register")); + request->setParameter("username", username); + request->setParameter("password", password); + request->setParameter("password_confirm", password_confirm); + request->setParameter("email", email); + request->setParameter("terms", std::string("on")); + HTTPManager::get()->addRequest(request); + return request; + } + + // ============================================================================ + void CurrentUser::requestSavedSession() + { + SignInRequest * request = NULL; + if(m_state == US_SIGNED_OUT && UserConfigParams::m_saved_session) + { + request = new SignInRequest(true); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action",std::string("saved-session")); + request->setParameter("userid", UserConfigParams::m_saved_user); + request->setParameter("token", UserConfigParams::m_saved_token.c_str()); + HTTPManager::get()->addRequest(request); + m_state = US_SIGNING_IN; + } + } + + CurrentUser::SignInRequest * CurrentUser::requestSignIn( const irr::core::stringw &username, + const irr::core::stringw &password, + bool save_session, bool request_now) + { + assert(m_state == US_SIGNED_OUT); + m_save_session = save_session; + SignInRequest * request = new SignInRequest(request_now); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action",std::string("connect")); + request->setParameter("username",username); + request->setParameter("password",password); + request->setParameter("save-session", StringUtils::boolstr(save_session)); + if (request_now) + { + HTTPManager::get()->addRequest(request); + m_state = US_SIGNING_IN; + } + return request; + } + + void CurrentUser::signIn(bool success, const XMLNode * input) + { + if (success) + { + int token_fetched = input->get("token", &m_token); + irr::core::stringw username(""); + int username_fetched = input->get("username", &username); + uint32_t userid(0); + int userid_fetched = input->get("userid", &userid); + m_profile = new Profile(userid, username, true); + assert(token_fetched && username_fetched && userid_fetched); + m_state = US_SIGNED_IN; + if(getSaveSession()) + { + UserConfigParams::m_saved_user = getID(); + UserConfigParams::m_saved_token = getToken(); + UserConfigParams::m_saved_session = true; + } + ProfileManager::get()->addPersistent(m_profile); + AchievementsManager::get()->updateCurrentPlayer(); + std::string achieved_string(""); + if(input->get("achieved", &achieved_string) == 1) + { + std::vector achieved_ids = StringUtils::splitToUInt(achieved_string, ' '); + AchievementsManager::get()->getActive()->sync(achieved_ids); + } + m_profile->fetchFriends(); + } + else + { + m_state = US_SIGNED_OUT; + } + } + + void CurrentUser::SignInRequest::callback() + { + CurrentUser::get()->signIn(m_success, m_result); + if(GUIEngine::ModalDialog::isADialogActive()) + { + LoginDialog * dialog = dynamic_cast(GUIEngine::ModalDialog::getCurrent()); + if(dialog != NULL) + { + if(m_success) + dialog->success(); + else + dialog->error(m_info); + } + } + } + + // ============================================================================ + + const CurrentUser::ServerCreationRequest * CurrentUser::requestServerCreation( const irr::core::stringw &name, + int max_players) + { + assert(m_state == US_SIGNED_IN); + ServerCreationRequest * request = new ServerCreationRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("create_server")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("name", name); + request->setParameter("max_players", max_players); + HTTPManager::get()->addRequest(request); + return request; + } + + void CurrentUser::ServerCreationRequest::callback() + { + if(m_success) + { + Server * server = new Server(*m_result->getNode("server")); + ServersManager::get()->addServer(server); + m_created_server_id = server->getServerId(); + } + } + + // ============================================================================ + void CurrentUser::requestSignOut(){ + assert(m_state == US_SIGNED_IN || m_state == US_GUEST); + SignOutRequest * request = new SignOutRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action",std::string("disconnect")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + HTTPManager::get()->addRequest(request); + m_state = US_SIGNING_OUT; + } + + void CurrentUser::signOut(bool success, const XMLNode * input) + { + if(!success) + { + Log::warn("CurrentUser::signOut", "%s", "There were some connection issues while signing out. Report a bug if this caused issues."); + } + m_token = ""; + ProfileManager::get()->clearPersistent(); + m_profile = NULL; + m_state = US_SIGNED_OUT; + UserConfigParams::m_saved_user = 0; + UserConfigParams::m_saved_token = ""; + UserConfigParams::m_saved_session = false; + AchievementsManager::get()->updateCurrentPlayer(); + } + + void CurrentUser::SignOutRequest::callback() + { + CurrentUser::get()->signOut(m_success, m_result); + } + + // ============================================================================ + + CurrentUser::ServerJoinRequest * CurrentUser::requestServerJoin(uint32_t server_id, + bool request_now) + { + assert(m_state == US_SIGNED_IN || m_state == US_GUEST); + ServerJoinRequest * request = new ServerJoinRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + request->setParameter("action",std::string("request-connection")); + request->setParameter("token", getToken()); + request->setParameter("id", getID()); + request->setParameter("server_id", server_id); + if (request_now) + HTTPManager::get()->addRequest(request); + return request; + } + + void CurrentUser::ServerJoinRequest::callback() + { + if(m_success) + { + uint32_t server_id; + m_result->get("serverid", &server_id); + ServersManager::get()->setJoinedServer(server_id); + } + //FIXME needs changes for actual valid joining + } + + // ============================================================================ + + const XMLRequest * CurrentUser::requestGetAddonVote( const std::string & addon_id) const + { + assert(m_state == US_SIGNED_IN); + XMLRequest * request = new XMLRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("get-addon-vote")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("addonid", addon_id.substr(6)); + HTTPManager::get()->addRequest(request); + return request; + } + + // ============================================================================ + /** + * A request to the server, to fetch matching results for the supplied search term. + * \param search_string the string to search for. + */ + const XMLRequest * CurrentUser::requestUserSearch( const irr::core::stringw & search_string) const + { + assert(m_state == US_SIGNED_IN); + XMLRequest * request = new XMLRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("user-search")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("search-string", search_string); + HTTPManager::get()->addRequest(request); + return request; + } + + // ============================================================================ + /** + * A request to the server, to perform a vote on an addon. + * \param addon_id the id of the addon to vote for. + * \param rating the voted rating. + */ + const CurrentUser::SetAddonVoteRequest * CurrentUser::requestSetAddonVote( const std::string & addon_id, float rating) const + { + assert(m_state == US_SIGNED_IN); + CurrentUser::SetAddonVoteRequest * request = new CurrentUser::SetAddonVoteRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("set-addon-vote")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("addonid", addon_id.substr(6)); + request->setParameter("rating", rating); + HTTPManager::get()->addRequest(request); + return request; + } + + /** + * Callback for the request to vote for an addon. Updates the local average rating. + */ + void CurrentUser::SetAddonVoteRequest::callback() + { + if(m_success) + { + std::string addon_id; + m_result->get("addon-id", &addon_id); + float average; + m_result->get("new-average", &average); + addons_manager->getAddon(Addon::createAddonId(addon_id))->setRating(average); + } + } + + // ============================================================================ + /** + * A request to the server, to invite a user to be friends. + * \param friend_id The id of the user which has to be friended. + */ + void CurrentUser::requestFriendRequest(const uint32_t friend_id) const + { + assert(m_state == US_SIGNED_IN); + CurrentUser::FriendRequest * request = new CurrentUser::FriendRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("friend-request")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("friendid", friend_id); + HTTPManager::get()->addRequest(request); + } + + /** + * Callback for the request to send a friend invitation. Shows a confirmation message and takes care of updating all the cached information. + */ + void CurrentUser::FriendRequest::callback() + { + uint32_t id(0); + m_result->get("friendid", &id); + irr::core::stringw info_text(""); + if(m_success) + { + CurrentUser::get()->getProfile()->addFriend(id); + ProfileManager::get()->getProfileByID(id)->setRelationInfo(new Profile::RelationInfo(_("Today"), false, true, false)); + OnlineProfileFriends::getInstance()->refreshFriendsList(); + info_text = _("Friend request send!"); + } + else + info_text = m_info; + GUIEngine::DialogQueue::get()->pushDialog( new UserInfoDialog(id, info_text,!m_success, true), true); + } + + // ============================================================================ + /** + * A request to the server, to accept a friend request. + * \param friend_id The id of the user of which the request has to be accepted. + */ + void CurrentUser::requestAcceptFriend(const uint32_t friend_id) const + { + assert(m_state == US_SIGNED_IN); + CurrentUser::AcceptFriendRequest * request = new CurrentUser::AcceptFriendRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("accept-friend-request")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("friendid", friend_id); + HTTPManager::get()->addRequest(request); + } + + /** + * Callback for the request to accept a friend invitation. Shows a confirmation message and takes care of updating all the cached information. + */ + void CurrentUser::AcceptFriendRequest::callback() + { + uint32_t id(0); + m_result->get("friendid", &id); + irr::core::stringw info_text(""); + if(m_success) + { + Profile * profile = ProfileManager::get()->getProfileByID(id); + profile->setFriend(); + profile->setRelationInfo(new Profile::RelationInfo(_("Today"), false, false, true)); + OnlineProfileFriends::getInstance()->refreshFriendsList(); + info_text = _("Friend request accepted!"); + } + else + info_text = m_info; + GUIEngine::DialogQueue::get()->pushDialog( new UserInfoDialog(id, info_text,!m_success, true), true); + } + + // ============================================================================ + /** + * A request to the server, to decline a friend request. + * \param friend_id The id of the user of which the request has to be declined. + */ + void CurrentUser::requestDeclineFriend(const uint32_t friend_id) const + { + assert(m_state == US_SIGNED_IN); + CurrentUser::DeclineFriendRequest * request = new CurrentUser::DeclineFriendRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("decline-friend-request")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("friendid", friend_id); + HTTPManager::get()->addRequest(request); + } + + /** + * Callback for the request to decline a friend invitation. Shows a confirmation message and takes care of updating all the cached information. + */ + void CurrentUser::DeclineFriendRequest::callback() + { + uint32_t id(0); + m_result->get("friendid", &id); + irr::core::stringw info_text(""); + if(m_success) + { + CurrentUser::get()->getProfile()->removeFriend(id); + ProfileManager::get()->moveToCache(id); + ProfileManager::get()->getProfileByID(id)->deleteRelationalInfo(); + OnlineProfileFriends::getInstance()->refreshFriendsList(); + info_text = _("Friend request declined!"); + } + else + info_text = m_info; + GUIEngine::DialogQueue::get()->pushDialog( new UserInfoDialog(id, info_text,!m_success, true), true); + + } + + // ============================================================================ + /** + * A request to the server, to cancel a pending friend request. + * \param friend_id The id of the user of which the request has to be canceled. + */ + void CurrentUser::requestCancelFriend(const uint32_t friend_id) const + { + assert(m_state == US_SIGNED_IN); + CurrentUser::CancelFriendRequest * request = new CurrentUser::CancelFriendRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("cancel-friend-request")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("friendid", friend_id); + HTTPManager::get()->addRequest(request); + } + + /** + * Callback for the request to cancel a friend invitation. Shows a confirmation message and takes care of updating all the cached information. + */ + void CurrentUser::CancelFriendRequest::callback() + { + uint32_t id(0); + m_result->get("friendid", &id); + irr::core::stringw info_text(""); + if(m_success) + { + CurrentUser::get()->getProfile()->removeFriend(id); + ProfileManager::get()->moveToCache(id); + ProfileManager::get()->getProfileByID(id)->deleteRelationalInfo(); + OnlineProfileFriends::getInstance()->refreshFriendsList(); + info_text = _("Friend request cancelled!"); + } + else + info_text = m_info; + GUIEngine::DialogQueue::get()->pushDialog( new UserInfoDialog(id, info_text,!m_success, true), true); + + } + + // ============================================================================ + /** + * A request to the server, to remove a friend relation. + * \param friend_id The id of the friend to be removed. + */ + void CurrentUser::requestRemoveFriend(const uint32_t friend_id) const + { + assert(m_state == US_SIGNED_IN); + CurrentUser::RemoveFriendRequest * request = new CurrentUser::RemoveFriendRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("remove-friend")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("friendid", friend_id); + HTTPManager::get()->addRequest(request); + } + + /** + * Callback for the request to remove a friend. Shows a confirmation message and takes care of updating all the cached information. + */ + void CurrentUser::RemoveFriendRequest::callback() + { + uint32_t id(0); + m_result->get("friendid", &id); + irr::core::stringw info_text(""); + if(m_success) + { + CurrentUser::get()->getProfile()->removeFriend(id); + ProfileManager::get()->moveToCache(id); + ProfileManager::get()->getProfileByID(id)->deleteRelationalInfo(); + OnlineProfileFriends::getInstance()->refreshFriendsList(); + info_text = _("Friend removed!"); + } + else + info_text = m_info; + GUIEngine::DialogQueue::get()->pushDialog( new UserInfoDialog(id, info_text,!m_success, true), true); + + } + + // ============================================================================ + /** + * A request to the server, to change the password of the signed in user. + * \param current_password The active password of the currently signed in user. + * \param new_password The password the user wants to change to. + * \param new_password_ver Confirmation of that password. Has to be the exact same. + */ + void CurrentUser::requestPasswordChange(const irr::core::stringw ¤t_password, + const irr::core::stringw &new_password, + const irr::core::stringw &new_password_ver) const + { + assert(m_state == US_SIGNED_IN); + ChangePasswordRequest * request = new ChangePasswordRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("change_password")); + request->setParameter("userid", getID()); + request->setParameter("current", current_password); + request->setParameter("new1", new_password); + request->setParameter("new2", new_password_ver); + HTTPManager::get()->addRequest(request); + } + + /** + * Callback for the change password request. If the matching dialog is still open, show a confirmation message. + */ + void CurrentUser::ChangePasswordRequest::callback() + { + if(GUIEngine::ModalDialog::isADialogActive()) + { + ChangePasswordDialog * dialog = dynamic_cast(GUIEngine::ModalDialog::getCurrent()); + if(dialog != NULL) + { + if(m_success) + dialog->success(); + else + dialog->error(m_info); + } + } + } + // ============================================================================ + /** + * Sends a request to the server to see if any new information is available. (online friends, notifications, etc.). + */ + void CurrentUser::requestPoll() const + { + assert(m_state == US_SIGNED_IN); + CurrentUser::PollRequest * request = new CurrentUser::PollRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("poll")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + HTTPManager::get()->addRequest(request); + } + + /** + * Callback for the poll request. Parses the information and spawns notifications accordingly. + */ + void CurrentUser::PollRequest::callback() + { + if(m_success) + { + if(!CurrentUser::get()->isRegisteredUser()) + return; + if(CurrentUser::get()->getProfile()->hasFetchedFriends()) + { + std::string online_friends_string(""); + if(m_result->get("online", &online_friends_string) == 1) + { + std::vector online_friends = StringUtils::splitToUInt(online_friends_string, ' '); + bool went_offline = false; + std::vector friends = CurrentUser::get()->getProfile()->getFriends(); + std::vector to_notify; + for(unsigned int i = 0; i < friends.size(); ++i) + { + bool now_online = false; + std::vector::iterator iter = + std::find(online_friends.begin(),online_friends.end(), friends[i]); + if (iter != online_friends.end()) + { + now_online = true; + online_friends.erase(iter); + } + Profile * profile = ProfileManager::get()->getProfileByID(friends[i]); + Profile::RelationInfo * relation_info = profile->getRelationInfo(); + if( relation_info->isOnline() ) + { + if (!now_online) + { + relation_info->setOnline(false); + went_offline = true; + } + } + else + { + if (now_online) + { + //User came online + relation_info->setOnline(true); + profile->setFriend(); //Do this because a user might have accepted a pending friend request. + to_notify.push_back(profile->getUserName()); + } + } + + } + + if(to_notify.size() > 0) + { + irr::core::stringw message(""); + if(to_notify.size() == 1) + { + message = to_notify[0] + irr::core::stringw(_(" is now online.")); + } + else if(to_notify.size() == 2) + { + message = to_notify[0] + irr::core::stringw(_(" and ")) + to_notify[1] + irr::core::stringw(_(" are now online.")); + } + else if(to_notify.size() == 3) + { + message = to_notify[0] + irr::core::stringw(_(", ")) + to_notify[1] + irr::core::stringw(_(" and ")) + to_notify[2] + irr::core::stringw(_(" are now online.")); + } + else if(to_notify.size() > 3) + { + message = StringUtils::toWString(to_notify.size()) + irr::core::stringw(_(" friends are now online.")); + } + GUIEngine::DialogQueue::get()->pushDialog( new NotificationDialog(NotificationDialog::T_Friends, message), false); + OnlineProfileFriends::getInstance()->refreshFriendsList(); + } + else if(went_offline) + { + OnlineProfileFriends::getInstance()->refreshFriendsList(); + } + } + } + else + { + CurrentUser::get()->getProfile()->fetchFriends(); + } + + int friend_request_count = 0; + for(unsigned int i = 0; i < m_result->getNumNodes(); i++) + { + const XMLNode * node = m_result->getNode(i); + if(node->getName() == "new_friend_request") + { + Profile::RelationInfo * ri = new Profile::RelationInfo("New", false, true, true); + Profile * p = new Profile(node); + p->setRelationInfo(ri); + ProfileManager::get()->addPersistent(p); + friend_request_count++; + } + } + if(friend_request_count > 0) + { + irr::core::stringw message(""); + if(friend_request_count > 1) + { + message = irr::core::stringw(_("You have ")) + StringUtils::toWString(friend_request_count) + irr::core::stringw(_(" new friend requests!.")); + } + else + { + message = _("You have a new friend request!"); + } + GUIEngine::DialogQueue::get()->pushDialog( new NotificationDialog(NotificationDialog::T_Friends, message), false); + OnlineProfileFriends::getInstance()->refreshFriendsList(); + } + } + // FIXME show connection error?? + // Perhaps show something after 2 misses. + + } + // ============================================================================ + /** + * Sends a message to the server that the client has been closed, if a user is signed in. + */ + void CurrentUser::onSTKQuit() const + { + if(isRegisteredUser()) + { + HTTPRequest * request = new HTTPRequest(true, HTTPManager::HTTP_MAX_PRIORITY); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("client-quit")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + HTTPManager::get()->addRequest(request); + } + } + + // ============================================================================ + /** + * Sends a confirmation to the server that an achievement has been completed, if a user is signed in. + * \param achievement_id the id of the achievement that got completed + */ + void CurrentUser::onAchieving(uint32_t achievement_id) const + { + if(isRegisteredUser()) + { + HTTPRequest * request = new HTTPRequest(true); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action", std::string("achieving")); + request->setParameter("token", getToken()); + request->setParameter("userid", getID()); + request->setParameter("achievementid", achievement_id); + HTTPManager::get()->addRequest(request); + } + } + + // ============================================================================ + /** \return the username if signed in. */ + irr::core::stringw CurrentUser::getUserName() const + { + if((m_state == US_SIGNED_IN ) || (m_state == US_GUEST)) + { + assert(m_profile != NULL); + return m_profile->getUserName(); + } + return _("Currently not signed in"); + } + + // ============================================================================ + /** \return the online id. */ + uint32_t CurrentUser::getID() const + { + if((m_state == US_SIGNED_IN )) + { + assert(m_profile != NULL); + return m_profile->getID(); + } + return 0; + } +} // namespace Online diff --git a/src/online/current_user.hpp b/src/online/current_user.hpp new file mode 100644 index 000000000..f220ae84a --- /dev/null +++ b/src/online/current_user.hpp @@ -0,0 +1,203 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_CURRENT_ONLINE_USER_HPP +#define HEADER_CURRENT_ONLINE_USER_HPP + +#include "online/http_manager.hpp" +#include "online/server.hpp" +#include "online/profile.hpp" +#include "utils/types.hpp" +#include "utils/synchronised.hpp" + +#include + +#include +#include + +namespace Online{ + + // ============================================================================ + + /** + * \brief Class that represents an online registered user + * \ingroup online + */ + class CurrentUser + { + public: + enum UserState + { + US_SIGNED_OUT = 0, + US_SIGNED_IN, + US_GUEST, + US_SIGNING_IN, + US_SIGNING_OUT + }; + + class SignInRequest : public XMLRequest + { + virtual void callback (); + public: + SignInRequest(bool manage_memory = false) : XMLRequest(manage_memory) {} + }; + + class SignOutRequest : public XMLRequest + { + virtual void callback (); + public: + SignOutRequest() : XMLRequest(true) {} + }; + + class ServerCreationRequest : public XMLRequest { + virtual void callback (); + uint32_t m_created_server_id; + public: + ServerCreationRequest() : XMLRequest() {} + const uint32_t getCreatedServerID() const { assert(isDone()); return m_created_server_id;} + }; + + class ServerJoinRequest : public XMLRequest { + virtual void callback (); + public: + ServerJoinRequest() : XMLRequest() {} + }; + + class SetAddonVoteRequest : public XMLRequest { + virtual void callback (); + public: + SetAddonVoteRequest() : XMLRequest() {} + }; + + class FriendRequest : public XMLRequest { + virtual void callback (); + public: + FriendRequest() : XMLRequest(true) {} + }; + + class AcceptFriendRequest : public XMLRequest { + virtual void callback (); + public: + AcceptFriendRequest() : XMLRequest(true) {} + }; + + class DeclineFriendRequest : public XMLRequest { + virtual void callback (); + public: + DeclineFriendRequest() : XMLRequest(true) {} + }; + + class RemoveFriendRequest : public XMLRequest { + virtual void callback (); + public: + RemoveFriendRequest() : XMLRequest(true) {} + }; + + class CancelFriendRequest : public XMLRequest { + virtual void callback (); + public: + CancelFriendRequest() : XMLRequest(true) {} + }; + + class PollRequest : public XMLRequest { + virtual void callback (); + public: + PollRequest() : XMLRequest(true) {} + }; + + class ChangePasswordRequest : public XMLRequest + { + virtual void callback (); + public: + ChangePasswordRequest() : XMLRequest(true) {} + }; + + + private: + std::string m_token; + bool m_save_session; + UserState m_state; + Profile * m_profile; + + bool getSaveSession() const { return m_save_session; } + + CurrentUser(); + + void signIn (bool success, const XMLNode * input); + void signOut (bool success, const XMLNode * input); + + public: + /**Singleton */ + static CurrentUser * get(); + static void deallocate(); + + void requestSavedSession(); + SignInRequest * requestSignIn( const irr::core::stringw &username, + const irr::core::stringw &password, + bool save_session, + bool request_now = true); + void requestSignOut(); + const ServerCreationRequest * requestServerCreation(const irr::core::stringw &name, int max_players); + ServerJoinRequest * requestServerJoin(uint32_t server_id, bool request_now = true); + + + /** Register */ + const XMLRequest * requestSignUp( const irr::core::stringw &username, + const irr::core::stringw &password, + const irr::core::stringw &password_ver, + const irr::core::stringw &email, + bool terms); + + const XMLRequest * requestRecovery(const irr::core::stringw &username, + const irr::core::stringw &email); + + const XMLRequest * requestGetAddonVote(const std::string & addon_id) const; + const SetAddonVoteRequest * requestSetAddonVote(const std::string & addon_id, float rating) const; + void requestFriendRequest(const uint32_t friend_id) const; + void requestAcceptFriend(const uint32_t friend_id) const; + void requestDeclineFriend(const uint32_t friend_id) const; + void requestRemoveFriend(const uint32_t friend_id) const; + void requestCancelFriend(const uint32_t friend_id) const; + void requestPasswordChange( const irr::core::stringw ¤t_password, + const irr::core::stringw &new_password, + const irr::core::stringw &new_password_ver) const; + + const XMLRequest * requestUserSearch(const irr::core::stringw & search_string) const; + + void onSTKQuit() const; + void onAchieving(uint32_t achievement_id) const; + void requestPoll() const; + + irr::core::stringw getUserName() const; + uint32_t getID() const; + /** Returns the user state. */ + const UserState getUserState() const { return m_state; } + /** Returns whether a user is signed in or not. */ + bool isRegisteredUser() const { return m_state == US_SIGNED_IN; } + /** Returns the session token of the signed in user. */ + const std::string & getToken() const { return m_token; } + /** Returns a pointer to the profile associated with the current user. */ + Profile * getProfile() const { return m_profile; } + + }; // class CurrentUser + +} // namespace Online + +#endif + +/*EOF*/ diff --git a/src/online/http_manager.cpp b/src/online/http_manager.cpp new file mode 100644 index 000000000..9f47684e6 --- /dev/null +++ b/src/online/http_manager.cpp @@ -0,0 +1,300 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010 Lucas Baudin +// 2011 Joerg Henrichs +// 2013 Glenn De Jonghe +// +// 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 "online/http_manager.hpp" + +#include "online/current_user.hpp" +#include "states_screens/state_manager.hpp" + +#include +#include +#include +#include + +#if defined(WIN32) && !defined(__CYGWIN__) +# include +#else +# include +# include +#endif + +using namespace Online; + +namespace Online{ + #define MENU_POLLING_INTERVAL 10.0f + #define GAME_POLLING_INTERVAL 15.0f + + static HTTPManager * http_singleton = NULL; + + HTTPManager* HTTPManager::get() + { + if (http_singleton == NULL) + { + http_singleton = new HTTPManager(); + } + return http_singleton; + } // get + + void HTTPManager::deallocate() + { + if (http_singleton != NULL) + { + delete http_singleton; + http_singleton = NULL; + } + } // deallocate + + bool HTTPManager::isRunning() + { + return http_singleton != NULL; + } + + + HTTPManager::HTTPManager(){ + curl_global_init(CURL_GLOBAL_DEFAULT); + pthread_cond_init(&m_cond_request, NULL); + m_abort.setAtomic(false); + m_time_since_poll = MENU_POLLING_INTERVAL * 0.9; + } + + // ============================================================================ + HTTPManager::~HTTPManager(){ + m_thread_id.lock(); + pthread_join(*m_thread_id.getData(), NULL); + delete m_thread_id.getData(); + m_thread_id.unlock(); + pthread_cond_destroy(&m_cond_request); + curl_global_cleanup(); + } + + + // --------------------------------------------------------------------------- + /** Start the actual network thread. This can not be done as part of + * the constructor, since the assignment to the global network_http + * variable has not been assigned at that stage, and the thread might + * use network_http - a very subtle race condition. So the thread can + * only be started after the assignment (in main) has been done. + */ + void HTTPManager::startNetworkThread() + { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + // Should be the default, but just in case: + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + m_thread_id.setAtomic(new pthread_t()); + int error = pthread_create(m_thread_id.getData(), &attr, + &HTTPManager::mainLoop, this); + if(error) + { + m_thread_id.lock(); + delete m_thread_id.getData(); + m_thread_id.unlock(); + m_thread_id.setAtomic(0); + Log::error("HTTP Manager", "Could not create thread, error=%d.\n", errno); + } + pthread_attr_destroy(&attr); + CurrentUser::get()->requestSavedSession(); + } // startNetworkThread + + + // --------------------------------------------------------------------------- + /** This function inserts a high priority request to quit into the request + * queue of the network thead, and also aborts any ongoing download. + * Separating this allows more time for the thread to finish cleanly, + * before it gets cancelled in the destructor. + */ + void HTTPManager::stopNetworkThread() + { + // If a download should be active (which means it was cancelled by the + // user, in which case it will still be ongoing in the background) + // we can't get the mutex, and would have to wait for a timeout, + // and we couldn't finish STK. This way we request an abort of + // a download, which mean we can get the mutex and ask the service + // thread here to cancel properly. + //cancelAllDownloads(); FIXME if used this way it also cancels the client-quit action + CurrentUser::get()->onSTKQuit(); + addRequest(new Request(true, HTTP_MAX_PRIORITY, Request::RT_QUIT)); + } // stopNetworkThread + + + // ---------------------------------------------------------------------------- + /** Signals to the progress function to request any ongoing download to be + * cancelled. This function can also be called if there is actually no + * download atm. The function progressDownload checks m_abort and will + * return a non-zero value which causes libcurl to abort. */ + void HTTPManager::cancelAllDownloads() + { + m_abort.setAtomic(true); + // FIXME doesn't get called at the moment. When using this again, + // be sure that HTTP_MAX_PRIORITY requests still get executed. + } // cancelAllDownloads + + + // ---------------------------------------------------------------------------- + /** Inserts a request into the queue of all requests. The request will be + * sorted by priority. + * \param request The pointer to the new request to insert. + */ + void HTTPManager::addRequest(Request *request) + { + assert(request->isAllowedToAdd()); + request->setBusy(); + m_request_queue.lock(); + m_request_queue.getData().push(request); + // Wake up the network http thread + pthread_cond_signal(&m_cond_request); + m_request_queue.unlock(); + } // addRequest + + + // ---------------------------------------------------------------------------- + /** Immediately performs a request synchronously + * \param request The pointer to the request to execute. + */ + void HTTPManager::synchronousRequest(Request *request) + { + assert(request->isAllowedToAdd()); + request->setBusy(); + request->execute(); + request->callback(); + request->setDone(); + } // synchronousRequest + + // --------------------------------------------------------------------------- + /** The actual main loop, which is started as a separate thread from the + * constructor. After testing for a new server, fetching news, the list + * of packages to download, it will wait for commands to be issued. + * \param obj: A pointer to this object, passed on by pthread_create + */ + void *HTTPManager::mainLoop(void *obj) + { + HTTPManager *me = (HTTPManager*) obj; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + + me->m_current_request = NULL; + me->m_request_queue.lock(); + while( me->m_request_queue.getData().empty() || + me->m_request_queue.getData().top()->getType() != Request::RT_QUIT) + { + bool empty = me->m_request_queue.getData().empty(); + // Wait in cond_wait for a request to arrive. The 'while' is necessary + // since "spurious wakeups from the pthread_cond_wait ... may occur" + // (pthread_cond_wait man page)! + while(empty) + { + pthread_cond_wait(&me->m_cond_request, me->m_request_queue.getMutex()); + empty = me->m_request_queue.getData().empty(); + } + me->m_current_request = me->m_request_queue.getData().top(); + me->m_request_queue.getData().pop(); + if(me->m_current_request->getType()==Request::RT_QUIT) + break; + me->m_request_queue.unlock(); + me->m_current_request->execute(); + me->addResult(me->m_current_request); + me->m_request_queue.lock(); + } // while + + // At this stage we have the lock for m_request_queue + while(!me->m_request_queue.getData().empty()) + { + Online::Request * request = me->m_request_queue.getData().top(); + me->m_request_queue.getData().pop(); + // Manage memory can be ignored here, all requests + // need to be freed. + delete request; + } + me->m_request_queue.unlock(); + pthread_exit(NULL); + return 0; + } // mainLoop + + // ---------------------------------------------------------------------------- + /** Inserts a request into the queue of results. + * \param request The pointer to the request to insert. + */ + void HTTPManager::addResult(Online::Request *request) + { + assert(request->isBusy()); + m_result_queue.lock(); + m_result_queue.getData().push(request); + m_result_queue.unlock(); + } + + // ---------------------------------------------------------------------------- + /** + * Takes a request out of the result queue, if any is present. + * Calls the callback method of the request and takes care of memory management if necessary. + */ + void HTTPManager::handleResultQueue() + { + Request * request = NULL; + m_result_queue.lock(); + if(!m_result_queue.getData().empty()) + { + request = m_result_queue.getData().front(); + m_result_queue.getData().pop(); + } + m_result_queue.unlock(); + if(request != NULL) + { + request->callback(); + if(request->manageMemory()) + { + delete request; + request = NULL; + } + else + request->setDone(); + } + } + + // ---------------------------------------------------------------------------- + /** + * Should be called every frame and takes care of processing the result queue + * and polling the database server if a user is signed in. + */ + void HTTPManager::update(float dt){ + handleResultQueue(); + + //Database polling starts here, only needed for registered users + if(!CurrentUser::get()->isRegisteredUser()) + return; + + m_time_since_poll += dt; + float interval = GAME_POLLING_INTERVAL; + if (StateManager::get()->getGameState() == GUIEngine::MENU) + interval = MENU_POLLING_INTERVAL; + if(m_time_since_poll > interval) + { + m_time_since_poll = 0; + CurrentUser::get()->requestPoll(); + } + + } +} // namespace Online + + + + diff --git a/src/online/http_manager.hpp b/src/online/http_manager.hpp new file mode 100644 index 000000000..2874c1dd3 --- /dev/null +++ b/src/online/http_manager.hpp @@ -0,0 +1,107 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010 Lucas Baudin +// 2011 Joerg Henrichs +// 2013 Glenn De Jonghe +// +// 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 HTTP_MANAGER_HPP +#define HTTP_MANAGER_HPP + +#include "io/xml_node.hpp" +#include "online/request.hpp" +#include "utils/string_utils.hpp" +#include "utils/synchronised.hpp" + +#include + +#include +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#endif + +#include +#include +#include + + +namespace Online{ + + /** + * \brief Class to connect with a server over HTTP(S) + * \ingroup online + */ + class HTTPManager + { + protected: + + float m_time_since_poll; + + /** The current requested being worked on. */ + Online::Request * m_current_request; + + /** A conditional variable to wake up the main loop. */ + pthread_cond_t m_cond_request; + + /** Signal an abort in case that a download is still happening. */ + Synchronised m_abort; + + /** Thread id of the thread running in this object. */ + Synchronised m_thread_id; + + /** The list of pointers to all requests that still need to be handled. */ + Synchronised< std::priority_queue < + Online::Request*, + std::vector, + Online::Request::Compare + > + > m_request_queue; + + /** The list of pointers to all requests that are already executed by the networking thread, but still need to be processed by the main thread. */ + Synchronised< std::queue > m_result_queue; + + void addResult(Online::Request *request); + void handleResultQueue(); + + static void *mainLoop(void *obj); + + HTTPManager(); //const std::string &url + ~HTTPManager(); + + public: + static const int HTTP_MAX_PRIORITY = 9999; + + // singleton + static HTTPManager* get(); + static void deallocate(); + static bool isRunning(); + + void synchronousRequest(Online::Request *request); + void addRequest(Online::Request *request); + void cancelAllDownloads(); + void startNetworkThread(); + void stopNetworkThread(); + + bool getAbort(){ return m_abort.getAtomic(); }; + void update(float dt); + + }; //class HTTPManager +} // namespace Online + +#endif // HTTP_MANAGER_HPP + +/*EOF*/ diff --git a/src/online/messages.cpp b/src/online/messages.cpp new file mode 100644 index 000000000..cd93e6742 --- /dev/null +++ b/src/online/messages.cpp @@ -0,0 +1,110 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "online/messages.hpp" +#include "utils/translation.hpp" +#include "utils/time.hpp" + +namespace Online +{ + namespace Messages + { + irr::core::stringw signingIn() + { + return irr::core::stringw(_("Signing in")) + loadingDots(); + } + // ------------------------------------------------------------------------ + + irr::core::stringw signingOut() + { + return irr::core::stringw(_("Signing out")) + loadingDots(); + } + // ------------------------------------------------------------------------ + irr::core::stringw validatingInfo() + { + return irr::core::stringw(_("Validating info")) + loadingDots(); + } + // ------------------------------------------------------------------------ + irr::core::stringw searching() + { + return irr::core::stringw(_("Searching")) + loadingDots(); + } + // ------------------------------------------------------------------------ + + irr::core::stringw signedInAs(const irr::core::stringw & name) + { + return irr::core::stringw(_("Signed in as : ")) + name + "."; + } + // ------------------------------------------------------------------------ + + irr::core::stringw joiningServer() + { + return irr::core::stringw(_("Joining server")) + loadingDots(); + } + // ------------------------------------------------------------------------ + + irr::core::stringw creatingServer() + { + return irr::core::stringw(_("Creating server")) + loadingDots(); + } + + // ------------------------------------------------------------------------ + + irr::core::stringw fetchingServers() + { + return irr::core::stringw(_("Fetching servers")) + loadingDots(); + } + + // ------------------------------------------------------------------------ + + irr::core::stringw fetchingFriends() + { + return irr::core::stringw(_("Fetching friends")) + loadingDots(); + } + + // ------------------------------------------------------------------------ + + irr::core::stringw fetchingAchievements() + { + return irr::core::stringw(_("Fetching achievements")) + loadingDots(); + } + + // ------------------------------------------------------------------------ + + irr::core::stringw processing() + { + return irr::core::stringw(_("Processing")) + loadingDots(); + } + + // ------------------------------------------------------------------------ + /** + * Shows a increasing number of dots. + * \param spaces Flag if unshowed dots should be replaced by spaces + * \param interval A float representing the time it takes to add a new dot + * \param max_dots The number of dots used. Defaults to 3. + */ + irr::core::stringw loadingDots(bool spaces, float interval, int max_dots) + { + int nr_dots = int(floor(StkTime::getRealTime() * (1 / interval))) % (max_dots+1); + return irr::core::stringw((std::string(nr_dots,'.') + std::string(max_dots-nr_dots,' ')).c_str()); + } + } // namespace messages +} // namespace Online + + +/* EOF */ diff --git a/src/online/messages.hpp b/src/online/messages.hpp new file mode 100644 index 000000000..4c73b9b3f --- /dev/null +++ b/src/online/messages.hpp @@ -0,0 +1,49 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ONLINE_MESSAGES_HPP +#define HEADER_ONLINE_MESSAGES_HPP + +#include +#include + +namespace Online +{ + /** + * \brief Messages to be shown related to API communication + * \ingroup online + */ + namespace Messages + { + irr::core::stringw loadingDots (bool spaces = true, float interval = 0.5f, int max_dots = 3); + irr::core::stringw signingIn (); + irr::core::stringw signingOut (); + irr::core::stringw validatingInfo (); + irr::core::stringw searching (); + irr::core::stringw joiningServer (); + irr::core::stringw creatingServer (); + irr::core::stringw fetchingServers (); + irr::core::stringw fetchingFriends (); + irr::core::stringw fetchingAchievements (); + irr::core::stringw processing (); + irr::core::stringw signedInAs (const irr::core::stringw & name); + } // namespace Messages +}// namespace Online +#endif + +/* EOF */ diff --git a/src/online/profile.cpp b/src/online/profile.cpp new file mode 100644 index 000000000..0b9c032d7 --- /dev/null +++ b/src/online/profile.cpp @@ -0,0 +1,281 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "online/profile.hpp" + +#include "online/profile_manager.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" +#include "online/current_user.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" + +#include +#include +#include + +using namespace Online; + +namespace Online{ + + + + Profile::RelationInfo::RelationInfo(const irr::core::stringw & date, bool is_online, bool is_pending, bool is_asker) + { + m_date = date; + m_is_online = is_online; + m_is_pending = is_pending; + m_is_asker = is_asker; + } + + // ============================================================================ + void Profile::RelationInfo::setOnline(bool online) + { + m_is_online = online; + if(m_is_online) + m_is_pending = false; + } + + // ============================================================================ + Profile::Profile( const uint32_t & userid, + const irr::core::stringw & username, + bool is_current_user) + { + m_state = S_READY; + m_cache_bit = true; + m_id = userid; + m_is_current_user = is_current_user; + m_username = username; + m_has_fetched_friends = false; + m_has_fetched_achievements = false; + m_relation_info = NULL; + m_is_friend = false; + } + + Profile::Profile(const XMLNode * xml, ConstructorType type) + { + m_relation_info = NULL; + m_is_friend = false; + if(type == C_RELATION_INFO){ + irr::core::stringw date(""); + xml->get("date", &date); + std::string is_pending_string(""); + xml->get("is_pending", &is_pending_string); + bool is_pending = is_pending_string == "yes"; + bool is_asker(false); + bool is_online(false); + if(is_pending) + { + std::string is_asker_string(""); + xml->get("is_asker", &is_asker_string); + is_asker = is_asker_string == "yes"; + } + else + { + std::string is_online_string(""); + xml->get("online", &is_online_string); + is_online = is_online_string == "yes"; + m_is_friend = true; + } + m_relation_info = new RelationInfo(date, is_online, is_pending, is_asker); + xml = xml->getNode("user"); + } + + xml->get("id", &m_id); + xml->get("user_name", &m_username); + m_cache_bit = true; + m_has_fetched_friends = false; + m_has_fetched_achievements = false; + m_is_current_user = (m_id == CurrentUser::get()->getID()); + m_state = S_READY; + } + // ============================================================================ + Profile::~Profile() + { + delete m_relation_info; + } + + // ============================================================================ + void Profile::fetchAchievements() + { + assert(CurrentUser::get()->isRegisteredUser()); + if(m_has_fetched_achievements || m_is_current_user) + return; + m_state = S_FETCHING; + requestAchievements(); + } + + // ============================================================================ + void Profile::achievementsCallback(const XMLNode * input) + { + m_achievements.clear(); + std::string achieved_string(""); + if(input->get("achieved", &achieved_string) == 1) + { + m_achievements = StringUtils::splitToUInt(achieved_string, ' '); + } + m_has_fetched_achievements = true; + m_state = S_READY; + Log::info("test","tit"); + } + + // ============================================================================ + + void Profile::requestAchievements() + { + assert(CurrentUser::get()->isRegisteredUser() && !m_is_current_user); + AchievementsRequest * request = new AchievementsRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action",std::string("get-achievements")); + request->setParameter("token", CurrentUser::get()->getToken()); + request->setParameter("userid", CurrentUser::get()->getID()); + request->setParameter("visitingid", m_id); + HTTPManager::get()->addRequest(request); + } + + void Profile::AchievementsRequest::callback() + { + uint32_t user_id(0); + m_result->get("visitingid", &user_id); + if( ProfileManager::get()->getProfileByID(user_id) != NULL ) + ProfileManager::get()->getProfileByID(user_id)->achievementsCallback(m_result); + } + + // ============================================================================ + void Profile::fetchFriends() + { + assert(CurrentUser::get()->isRegisteredUser()); + if(m_has_fetched_friends) + return; + m_state = S_FETCHING; + requestFriendsList(); + } + // ============================================================================ + void Profile::friendsListCallback(const XMLNode * input) + { + const XMLNode * friends_xml = input->getNode("friends"); + m_friends.clear(); + for (unsigned int i = 0; i < friends_xml->getNumNodes(); i++) + { + Profile * profile; + if(m_is_current_user) + { + profile = new Profile(friends_xml->getNode(i) , C_RELATION_INFO); + ProfileManager::get()->addPersistent(profile); + } + else + { + profile = new Profile(friends_xml->getNode(i)->getNode("user"), C_DEFAULT); + ProfileManager::get()->addToCache(profile); + } + m_friends.push_back(profile->getID()); + } + m_has_fetched_friends = true; + m_state = S_READY; + } + + // ============================================================================ + + void Profile::requestFriendsList() + { + assert(CurrentUser::get()->isRegisteredUser()); + FriendsListRequest * request = new FriendsListRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action",std::string("get-friends-list")); + request->setParameter("token", CurrentUser::get()->getToken()); + request->setParameter("userid", CurrentUser::get()->getID()); + request->setParameter("visitingid", m_id); + HTTPManager::get()->addRequest(request); + } + + void Profile::FriendsListRequest::callback() + { + uint32_t user_id(0); + m_result->get("visitingid", &user_id); + if( ProfileManager::get()->getProfileByID(user_id) != NULL ) + ProfileManager::get()->getProfileByID(user_id)->friendsListCallback(m_result); + } + + // ============================================================================ + + void Profile::removeFriend( const uint32_t id) + { + assert (m_has_fetched_friends); + std::vector::iterator iter; + for (iter = m_friends.begin(); iter != m_friends.end();) + { + if (*iter == id) + { + m_friends.erase(iter++); + break; + } + else + ++iter; + } + } + // ============================================================================ + + void Profile::addFriend( const uint32_t id) + { + assert (m_has_fetched_friends); + for(unsigned int i=0; i< m_friends.size(); i++) + if(m_friends[i] == id) + return; + m_friends.push_back(id); + } + + // ============================================================================ + + void Profile::deleteRelationalInfo() + { + delete m_relation_info; + m_relation_info = NULL; + } + + // ============================================================================ + const std::vector & Profile::getFriends() + { + assert (m_has_fetched_friends && m_state == S_READY); + return m_friends; + } + + // ============================================================================ + const std::vector & Profile::getAchievements() + { + assert (m_has_fetched_achievements && m_state == S_READY && !m_is_current_user); + return m_achievements; + } + + //============================================================================= + void Profile::merge(Profile * profile) + { + assert (profile != NULL); + if(!this->m_has_fetched_friends && profile->m_has_fetched_friends) + this->m_friends = profile->m_friends; + if(!this->m_has_fetched_achievements && profile->m_has_fetched_friends) + this->m_achievements = profile->m_achievements; + if(this->m_relation_info == NULL && profile->m_relation_info != NULL) + { + this->m_relation_info = profile->m_relation_info; + profile->m_relation_info = NULL; //We don't want the destructor of the profile instance to destroy the relation info + } + delete profile; + } + +} // namespace Online diff --git a/src/online/profile.hpp b/src/online/profile.hpp new file mode 100644 index 000000000..60f5b39b6 --- /dev/null +++ b/src/online/profile.hpp @@ -0,0 +1,154 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ONLINE_PROFILE_HPP +#define HEADER_ONLINE_PROFILE_HPP + +#include "http_manager.hpp" +#include "online/request.hpp" +#include "utils/types.hpp" +#include "utils/ptr_vector.hpp" + + +#include + +#include + +namespace Online{ + + // ============================================================================ + + /** + * \brief Class that represents an online profile + * \ingroup online + */ + class Profile + { + public : + enum ConstructorType + { + C_DEFAULT = 1, + C_RELATION_INFO + }; + class RelationInfo + { + private: + bool m_is_online; + bool m_is_pending; + bool m_is_asker; + irr::core::stringw m_date; + public: + RelationInfo(const irr::core::stringw & date, bool is_online, bool is_pending, bool is_asker = false); + bool isPending(){return m_is_pending;} + bool isAsker(){return m_is_asker;} + const irr::core::stringw & getDate() { return m_date; } + bool isOnline() const { return m_is_online; } + void setOnline(bool online); + }; + class FriendsListRequest : public XMLRequest + { + virtual void callback (); + public: + FriendsListRequest() : XMLRequest(0, true) {} + }; + class AchievementsRequest : public XMLRequest + { + virtual void callback (); + public: + AchievementsRequest() : XMLRequest(0, true) {} + }; + + typedef std::vector IDList; + private: + + enum State + { + S_FETCHING = 1, + S_READY + }; + + State m_state; + bool m_is_current_user; + uint32_t m_id; + irr::core::stringw m_username; + /** information about the relation with the current user */ + RelationInfo * m_relation_info; + /** Whether or not the user of this profile, is a friend of the current user */ + bool m_is_friend; + + bool m_has_fetched_friends; + /** + * List of user id's that are friends with the user of this profile. + * In case this profile is of the current user, this list also contains any id's of users that still have a friend request pending. + * */ + std::vector m_friends; + + bool m_has_fetched_achievements; + std::vector m_achievements; + + bool m_cache_bit; + + void requestFriendsList(); + void friendsListCallback(const XMLNode * input); + + void requestAchievements(); + void achievementsCallback(const XMLNode * input); + + public: + Profile( const uint32_t & userid, + const irr::core::stringw & username, + bool is_current_user = false); + Profile( const XMLNode * xml, + ConstructorType type = C_DEFAULT); + ~Profile(); + void fetchFriends(); + const std::vector & getFriends(); + bool hasFetchedFriends() { return m_has_fetched_friends;} + + void fetchAchievements(); + const std::vector & getAchievements(); + bool hasFetchedAchievements() { return m_has_fetched_achievements;} + + bool isFetching() const { return m_state == S_FETCHING; } + bool isReady() const { return m_state == S_READY; } + + bool isCurrentUser() const { return m_is_current_user; } + bool isFriend() const { return m_is_friend; } + void setFriend() { m_is_friend = true; } + void removeFriend(const uint32_t id); + void addFriend(const uint32_t id); + void deleteRelationalInfo(); + RelationInfo * getRelationInfo() { return m_relation_info; } + void setRelationInfo(RelationInfo * r){ delete m_relation_info; m_relation_info = r;} + + void setCacheBit(bool cache_bit) { m_cache_bit = cache_bit; } + bool getCacheBit() const { return m_cache_bit; } + + uint32_t getID() const { return m_id; } + const irr::core::stringw & getUserName() const { return m_username; } + + void merge(Profile * profile); + + + }; // class Profile + +} // namespace Online + +#endif + +/*EOF*/ diff --git a/src/online/profile_manager.cpp b/src/online/profile_manager.cpp new file mode 100644 index 000000000..142ae2d5e --- /dev/null +++ b/src/online/profile_manager.cpp @@ -0,0 +1,230 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "online/profile_manager.hpp" + +#include "online/current_user.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" + +#include +#include +#include + +using namespace Online; + +namespace Online{ + static ProfileManager* profile_manager_singleton(NULL); + + ProfileManager* ProfileManager::get() + { + if (profile_manager_singleton == NULL) + profile_manager_singleton = new ProfileManager(); + return profile_manager_singleton; + } + + void ProfileManager::deallocate() + { + delete profile_manager_singleton; + profile_manager_singleton = NULL; + } // deallocate + + // ============================================================================ + ProfileManager::ProfileManager() + { + assert(m_max_cache_size > 1); + m_currently_visiting = NULL; + } + + // ============================================================================ + ProfileManager::~ProfileManager() + { + clearPersistent(); + ProfilesMap::iterator it; + for ( it = m_profiles_cache.begin(); it != m_profiles_cache.end(); ++it ) { + delete it->second; + } + } + + // ============================================================================ + + void ProfileManager::iterateCache(Profile * profile) + { + if(m_profiles_cache.size() == m_max_cache_size) + { + profile->setCacheBit(true); + ProfilesMap::iterator iter; + for (iter = m_profiles_cache.begin(); iter != m_profiles_cache.end(); ++iter) + { + if (!iter->second->getCacheBit()) + return; + } + //All cache bits are one! Set them all to zero except the one currently being visited + for (iter = m_profiles_cache.begin(); iter != m_profiles_cache.end(); ++iter) + { + iter->second->setCacheBit(false); + } + profile->setCacheBit(true); + } + + } + + // ============================================================================ + /** Initialisation before the object is displayed. If necessary this function + * will pause the race if it is running (i.e. world exists). While only some + * of the screen can be shown during the race (via the in-game menu you + * can get the options screen and the help screens only). This is used by + * the RaceResultGUI to leave the race running (for the end animation) while + * the results are being shown. + */ + void ProfileManager::directToCache(Profile * profile) + { + assert(profile != NULL); + if(m_profiles_cache.size() == m_max_cache_size) + { + ProfilesMap::iterator iter; + for (iter = m_profiles_cache.begin(); iter != m_profiles_cache.end();) + { + if (!iter->second->getCacheBit()) + { + delete iter->second; + m_profiles_cache.erase(iter); + break; + } + else + ++iter; + } + } + m_profiles_cache[profile->getID()] = profile; + assert(m_profiles_cache.size() <= m_max_cache_size); + + } + + // ============================================================================ + /** + * Adds a profile to the persistent map. + * If a profile with the same id is already in there, the profiles are "merged" with as goal saving as much information. + * (i.e. one profile instance could have already fetched the friends, while the other could have fetched the achievements.) + */ + void ProfileManager::addPersistent(Profile * profile) + { + if(inPersistent(profile->getID())) + { + m_profiles_persistent[profile->getID()]->merge(profile); + } + else + { + m_profiles_persistent[profile->getID()] = profile; + } + } + // ============================================================================ + /** + * Removes and deletes an entry from the persistent map. + */ + void ProfileManager::deleteFromPersistent(const uint32_t id) + { + if (inPersistent(id)) + { + delete m_profiles_persistent[id]; + m_profiles_persistent.erase(id); + } + else + Log::warn("ProfileManager::removePersistent", "Tried to remove profile with id %d from persistent while not present", id); + } + + // ============================================================================ + + void ProfileManager::clearPersistent() + { + ProfilesMap::iterator it; + for ( it = m_profiles_persistent.begin(); it != m_profiles_persistent.end(); ++it ) { + delete it->second; + } + m_profiles_persistent.clear(); + } + + // ============================================================================ + + void ProfileManager::moveToCache(const uint32_t id) + { + if (inPersistent(id)) + { + Profile * profile = getProfileByID(id); + m_profiles_persistent.erase(id); + addToCache(profile); + } + else + Log::warn("ProfileManager::moveToCache", "Tried to move profile with id %d from persistent to cache while not present", id); + } + + // ============================================================================ + + void ProfileManager::addToCache(Profile * profile) + { + if(inPersistent(profile->getID())) + m_profiles_persistent[profile->getID()]->merge(profile); + else if(cacheHit(profile->getID())) + m_profiles_cache[profile->getID()]->merge(profile); + else + directToCache(profile); + } + + // ============================================================================ + + bool ProfileManager::inPersistent(const uint32_t id) + { + if (m_profiles_persistent.find(id) != m_profiles_persistent.end()) + return true; + return false; + } + + // ============================================================================ + + bool ProfileManager::cacheHit(const uint32_t id) + { + if (m_profiles_cache.find(id) != m_profiles_cache.end()) + { + iterateCache(m_profiles_cache[id]); + return true; + } + return false; + } + + // ============================================================================ + void ProfileManager::setVisiting(const uint32_t id) + { + m_currently_visiting = getProfileByID(id); + } + + // ============================================================================ + + Profile * ProfileManager::getProfileByID(const uint32_t id) + { + + if(inPersistent(id)) + return m_profiles_persistent[id]; + if(cacheHit(id)) + return m_profiles_cache[id]; + //FIXME not able to get! Now this should actually fetch the info from the server, but I haven't come up with a good asynchronous idea yet. + return NULL; + } + + + +} // namespace Online diff --git a/src/online/profile_manager.hpp b/src/online/profile_manager.hpp new file mode 100644 index 000000000..3f2e6a6f0 --- /dev/null +++ b/src/online/profile_manager.hpp @@ -0,0 +1,81 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ONLINE_PROFILE_MANAGER_HPP +#define HEADER_ONLINE_PROFILE_MANAGER_HPP + +#include "utils/types.hpp" +#include "online/profile.hpp" + + +#include + +#include + +namespace Online{ + /** + * \brief Class that takes care of online profiles + * \ingroup online + */ + class ProfileManager + { + + private: + ProfileManager (); + ~ProfileManager (); + + typedef std::map ProfilesMap; + + /** A map of profiles that is persistent. (i.e. no automatic removing happens) Normally used for the current user's profile and friends. */ + ProfilesMap m_profiles_persistent; + /** + * Any profiles that don't go into the persistent map, go here. + * Using a Least Recent Used caching algorithm with age bits to remove entries when the max size is reached. + **/ + ProfilesMap m_profiles_cache; + Profile * m_currently_visiting; + /** The max size of the m_profiles cache. */ + static const unsigned int m_max_cache_size = 20; + + void iterateCache(Profile * profile); + void directToCache(Profile * profile); + + public: + /**Singleton */ + static ProfileManager * get(); + static void deallocate(); + + void addToCache(Profile * profile); + void addPersistent(Profile * profile); + void deleteFromPersistent(const uint32_t id); + void clearPersistent(); + void moveToCache(const uint32_t id); + void setVisiting(const uint32_t id); + bool cacheHit(const uint32_t id); + bool inPersistent(const uint32_t id); + /** \return the instance of the profile that's currently being visited */ + Profile * getVisitingProfile() {return m_currently_visiting;} + Profile * getProfileByID(const uint32_t id); + + }; // class CurrentUser + +} // namespace Online + +#endif + +/*EOF*/ diff --git a/src/online/request.cpp b/src/online/request.cpp new file mode 100644 index 000000000..bc1899ca1 --- /dev/null +++ b/src/online/request.cpp @@ -0,0 +1,310 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// 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 "online/request.hpp" + +#include "online/http_manager.hpp" +#include "utils/translation.hpp" +#include "utils/constants.hpp" + +#ifdef WIN32 +# include +#endif + +#include + +#include + + +namespace Online{ + + class HTTPManager; + + // ========================================================================================= + /** + * Creates a request that can be handled by the HTTPManager + * \param manage_memory whether or not the HTTPManager should take care of deleting the object after all callbacks have been done + * \param priority by what priority should the HTTPManager take care of this request + * \param type indicates whether the request has a special task for the HTTPManager + */ + Request::Request(bool manage_memory, int priority, int type) + : m_type(type), m_manage_memory(manage_memory), m_priority(priority) + { + m_cancel.setAtomic(false); + m_state.setAtomic(S_PREPARING); + } // Request + + Request::~Request() + { + } + + void Request::execute() + { + assert(isBusy()); + prepareOperation(); + operation(); + afterOperation(); + } + + void Request::afterOperation() + { + } + + // ========================================================================================= + /** + * Creates a HTTP(S) request that will have a raw string as result. (Can ofcourse be used if the result doesn't matter.) + * \param manage_memory whether or not the HTTPManager should take care of deleting the object after all callbacks have been done + * \param priority by what priority should the HTTPManager take care of this request + */ + HTTPRequest::HTTPRequest(bool manage_memory, int priority) + : Request(manage_memory, priority, 0) + { + m_url = ""; + m_parameters = new Parameters(); + m_progress.setAtomic(0); + } + + HTTPRequest::~HTTPRequest() + { + delete m_parameters; + } + + /** + * Checks the request if it has enough (correct) information to be executed (and thus allowed to add to the queue) + */ + bool HTTPRequest::isAllowedToAdd() + { + if (!Request::isAllowedToAdd() || m_url.size() < 5 || ( m_url.substr(0, 5) != "http:")) + { + return false; + } + return true; + } + + void HTTPRequest::prepareOperation() + { + m_curl_session = curl_easy_init(); + if(!m_curl_session) + { + Log::error("HTTPRequest::prepareOperation", "LibCurl session not initialized."); + return; + } + curl_easy_setopt(m_curl_session, CURLOPT_URL, m_url.c_str()); + curl_easy_setopt(m_curl_session, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(m_curl_session, CURLOPT_WRITEFUNCTION, &HTTPRequest::WriteCallback); + curl_easy_setopt(m_curl_session, CURLOPT_NOPROGRESS, 0); + curl_easy_setopt(m_curl_session, CURLOPT_PROGRESSDATA, this); + curl_easy_setopt(m_curl_session, CURLOPT_PROGRESSFUNCTION, &HTTPRequest::progressDownload); + curl_easy_setopt(m_curl_session, CURLOPT_CONNECTTIMEOUT, 20); + curl_easy_setopt(m_curl_session, CURLOPT_LOW_SPEED_LIMIT, 10); + curl_easy_setopt(m_curl_session, CURLOPT_LOW_SPEED_TIME, 20); + //https + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, "Host: api.stkaddons.net"); + curl_easy_setopt(m_curl_session, CURLOPT_HTTPHEADER, chunk); + curl_easy_setopt(m_curl_session, CURLOPT_CAINFO, (file_manager->getDataDir() + "web.tuxfamily.org.pem").c_str()); + curl_easy_setopt(m_curl_session, CURLOPT_SSL_VERIFYPEER, 0L); + //curl_easy_setopt(m_curl_session, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(m_curl_session, CURLOPT_WRITEDATA, &m_string_buffer); + } + + void HTTPRequest::operation() + { + if(!m_curl_session) + return; + Parameters::iterator iter; + std::string postString(""); + for (iter = m_parameters->begin(); iter != m_parameters->end(); ++iter) + { + if(iter != m_parameters->begin()) + postString.append("&"); + char * escaped = curl_easy_escape(m_curl_session , iter->first.c_str(), iter->first.size()); + postString.append(escaped); + curl_free(escaped); + postString.append("="); + escaped = curl_easy_escape(m_curl_session , iter->second.c_str(), iter->second.size()); + postString.append(escaped); + curl_free(escaped); + } + Log::info( "HTTPRequest::operation", "Sending : %s", postString.c_str()); + curl_easy_setopt(m_curl_session, CURLOPT_POSTFIELDS, postString.c_str()); + std::string uagent( std::string("SuperTuxKart/") + STK_VERSION ); + #ifdef WIN32 + uagent += (std::string)" (Windows)"; + #elif defined(__APPLE__) + uagent += (std::string)" (Macintosh)"; + #elif defined(__FreeBSD__) + uagent += (std::string)" (FreeBSD)"; + #elif defined(linux) + uagent += (std::string)" (Linux)"; + #else + // Unknown system type + #endif + curl_easy_setopt(m_curl_session, CURLOPT_USERAGENT, uagent.c_str()); + + m_curl_code = curl_easy_perform(m_curl_session); + } + + void HTTPRequest::afterOperation() + { + if(m_curl_code == CURLE_OK) + setProgress(1.0f); + else + setProgress(-1.0f); + Request::afterOperation(); + curl_easy_cleanup(m_curl_session); + } + + size_t HTTPRequest::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) + { + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; + } + + /** + * \pre request has to be done + * \return get the result string from the request reply + */ + const std::string & HTTPRequest::getResult() const + { + assert(isDone()); + return m_string_buffer; + } + + // ---------------------------------------------------------------------------- + /** Callback function from curl: inform about progress. + * \param clientp + * \param download_total Total size of data to download. + * \param download_now How much has been downloaded so far. + * \param upload_total Total amount of upload. + * \param upload_now How muc has been uploaded so far. + */ + int HTTPRequest::progressDownload(void *clientp, + double download_total, double download_now, + double upload_total, double upload_now) + { + HTTPRequest *request = (HTTPRequest *)clientp; + + HTTPManager* http_manager = HTTPManager::get(); + + // Check if we are asked to abort the download. If so, signal this + // back to libcurl by returning a non-zero status. + if(http_manager->getAbort() || request->isCancelled() ) + { + // Indicates to abort the current download, which means that this + // thread will go back to the mainloop and handle the next request. + return 1; + } + + float f; + if(download_now < download_total) + { + f = (float)download_now / (float)download_total; + // In case of floating point rouding errors make sure that + // 1.0 is only reached when downloadFileInternal is finished + if (f>=1.0f) f=0.99f; + } + else + { + // Don't set progress to 1.0f; this is done in loadFileInternal + // after checking curls return code! + f= download_total==0 ? 0 : 0.99f; + } + request->setProgress(f); + return 0; + } // progressDownload + + // ========================================================================================= + /** + * Creates a HTTP(S) request that will automatically parse the answer into a XML structure + * \param manage_memory whether or not the HTTPManager should take care of deleting the object after all callbacks have been done + * \param priority by what priority should the HTTPManager take care of this request + */ + XMLRequest::XMLRequest(bool manage_memory, int priority) + : HTTPRequest(manage_memory, priority) + { + m_string_buffer = ""; + m_info = ""; + m_success = false; + m_result = NULL; + } + + XMLRequest::~XMLRequest() + { + delete m_result; + } + + void XMLRequest::prepareOperation() + { + HTTPRequest::prepareOperation(); + } + + + void XMLRequest::operation() + { + HTTPRequest::operation(); + m_result = file_manager->createXMLTreeFromString(m_string_buffer); + } + + void XMLRequest::afterOperation() + { + if(m_curl_code != CURLE_OK) + Log::error( "XMLRequest::afterOperation", "curl_easy_perform() failed: %s", curl_easy_strerror(m_curl_code)); + bool success = false; + std::string rec_success; + if(m_result->get("success", &rec_success)) + { + if (rec_success =="yes") + success = true; + m_result->get("info", &m_info); + } + else + m_info = _("Unable to connect to the server. Check your internet connection or try again later."); + m_success = success; + HTTPRequest::afterOperation(); + } + + /** + * \pre request has to be done + * \return get the complete result from the request reply + */ + const XMLNode * XMLRequest::getResult() const + { + assert(isDone()); + return m_result; + } + + /** + * \pre request has to be done + * \return get the info from the request reply + */ + const irr::core::stringw & XMLRequest::getInfo() const + { + assert(isDone()); + return m_info; + } + + /** + * \pre request has to be done + * \return whether or not the request was a success + */ + bool XMLRequest::isSuccess() const + { + assert(isDone()); + return m_success; + } + +} // namespace Online diff --git a/src/online/request.hpp b/src/online/request.hpp new file mode 100644 index 000000000..9543e8014 --- /dev/null +++ b/src/online/request.hpp @@ -0,0 +1,229 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2011 Joerg Henrichs +// 2013 Glenn De Jonghe +// +// 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 HEADER_ONLINE_REQUEST_HPP +#define HEADER_ONLINE_REQUEST_HPP + +#include "io/file_manager.hpp" +#include "utils/cpp2011.h" +#include "utils/string_utils.hpp" +#include "utils/synchronised.hpp" + +#ifdef WIN32 +# include +#endif +#include +#include +#include + +namespace Online{ + + /** + * Stores a request for the HTTP Manager. They will be sorted by priorities. + * \ingroup online + */ + class Request + { + private: + /** Type of the request + * Has 0 as default value. + * */ + const int m_type; + /** True if the memory for this Request should be managed by + * http connector (i.e. this object is freed once the request + * is handled). Otherwise the memory is not freed, so it must + * be freed by the calling function. */ + const bool m_manage_memory; + /** The priority of this request. The higher the value the more + important this request is. */ + const int m_priority; + + enum State + { + S_PREPARING, + S_BUSY, + S_DONE + }; + + protected: + + /** Cancel this request if it is active. */ + Synchronised m_cancel; + /** Set to though if the reply of the request is in and callbacks are executed */ + Synchronised m_state; + + virtual void prepareOperation() {} + virtual void operation() {} + virtual void afterOperation(); + + public: + enum RequestType + { + RT_QUIT = 1 + }; + + Request(bool manage_memory, int priority, int type); + virtual ~Request(); + + void execute(); + int getType() const { return m_type; } + // ------------------------------------------------------------------------ + /** Returns if the memory for this object should be managed by + * by network_http (i.e. freed once the request is handled). */ + bool manageMemory() const { return m_manage_memory; } + // ------------------------------------------------------------------------ + /** Returns the priority of this request. */ + int getPriority() const { return m_priority; } + // ------------------------------------------------------------------------ + /** Signals that this request should be canceled. */ + void cancel() { m_cancel.setAtomic(true); } + // ------------------------------------------------------------------------ + /** Returns if this request is to be canceled. */ + bool isCancelled() const { return m_cancel.getAtomic(); } + // ------------------------------------------------------------------------ + /** Returns if this request is done. */ + bool isDone() const { return m_state.getAtomic() == S_DONE; } + // ------------------------------------------------------------------------ + /** Should only be called by the manager */ + void setDone() { m_state.setAtomic(S_DONE); } + // ------------------------------------------------------------------------ + /** Returns if this request is being prepared. */ + bool isPreparing() const { return m_state.getAtomic() == S_PREPARING; } + // ------------------------------------------------------------------------ + /** Returns if this request is busy. */ + bool isBusy() const { return m_state.getAtomic() == S_BUSY; } + // ------------------------------------------------------------------------ + /** Sets the request stqte to busy. */ + void setBusy() { m_state.setAtomic(S_BUSY); } + // ------------------------------------------------------------------------ + /** Virtual method to check if a request has initialized all needed members to a valid value. */ + virtual bool isAllowedToAdd() const { return isPreparing(); } + + /** Executed when a request has finished. */ + virtual void callback() {} + + /** This class is used by the priority queue to sort requests by priority. + */ + class Compare + { + public: + /** Compares two requests, returns if the first request has a lower + * priority than the second one. */ + bool operator() (const Request *a, const Request *b) const + { + return a->getPriority() < b->getPriority(); + } + }; // Compare + }; // Request + + + // ======================================================================== + + + class HTTPRequest : public Request + { + + protected : + + typedef std::map Parameters; + + /** The progress indicator. 0 untill it is started and the first + * packet is downloaded. At the end either -1 (error) or 1 + * (everything ok) at the end. */ + Synchronised m_progress; + std::string m_url; + /** The POST parameters that will be send with the request. */ + Parameters * m_parameters; + CURL * m_curl_session; + CURLcode m_curl_code; + std::string m_string_buffer; + + virtual void prepareOperation() OVERRIDE; + virtual void operation() OVERRIDE; + virtual void afterOperation() OVERRIDE; + + + static int progressDownload( void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + + static size_t WriteCallback( void *contents, + size_t size, + size_t nmemb, + void *userp); + + + public : + HTTPRequest(bool manage_memory = false, int priority = 1); + virtual ~HTTPRequest(); + + void setParameter(const std::string & name, const std::string &value){ + assert(isPreparing()); + (*m_parameters)[name] = value; + }; + void setParameter(const std::string & name, const irr::core::stringw &value){ + assert(isPreparing()); + (*m_parameters)[name] = irr::core::stringc(value.c_str()).c_str(); + } + template + void setParameter(const std::string & name, const T& value){ + assert(isPreparing()); + (*m_parameters)[name] = StringUtils::toString(value); + } + + const std::string & getResult() const; + + /** Returns the current progress. */ + float getProgress() const { return m_progress.getAtomic(); } + /** Sets the current progress. */ + void setProgress(float f) { m_progress.setAtomic(f); } + + const std::string & getURL() { assert(isBusy()); return m_url;} + + void setURL(const std::string & url) { assert(isPreparing()); m_url = url;} + + virtual bool isAllowedToAdd() OVERRIDE; + + }; + + class XMLRequest : public HTTPRequest + { + protected : + XMLNode * m_result; + irr::core::stringw m_info; + bool m_success; + + virtual void prepareOperation() OVERRIDE; + virtual void operation() OVERRIDE; + virtual void afterOperation() OVERRIDE; + + public : + XMLRequest(bool manage_memory = false, int priority = 1); + virtual ~XMLRequest(); + + const XMLNode * getResult() const; + const irr::core::stringw & getInfo() const; + bool isSuccess() const; + + }; +} //namespace Online + +#endif + diff --git a/src/online/server.cpp b/src/online/server.cpp new file mode 100644 index 000000000..24ec5ff63 --- /dev/null +++ b/src/online/server.cpp @@ -0,0 +1,75 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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. +/** + \page online Online + */ + +#include "online/server.hpp" + +#include "io/xml_node.hpp" +#include "utils/constants.hpp" +#include "utils/string_utils.hpp" + +namespace Online{ + Server::SortOrder Server::m_sort_order=Server::SO_NAME; //FIXME change to some other default + + Server::Server(const XMLNode & xml) + { + assert(xml.getName() == "server"); + m_name = ""; + m_satisfaction_score = 0; + m_server_id = 0; + m_current_players = 0; + m_max_players = 0; + + xml.get("name", &m_lower_case_name); + m_name = StringUtils::decodeFromHtmlEntities(m_lower_case_name); + m_lower_case_name = StringUtils::toLowerCase(m_lower_case_name); + + xml.get("id", &m_server_id); + xml.get("hostid", &m_host_id); + xml.get("max_players", &m_max_players); + xml.get("current_players", &m_current_players); + + }; // Server(const XML&) + + // ---------------------------------------------------------------------------- + /** + * \brief Filter the add-on with a list of words. + * \param words A list of words separated by ' '. + * \return true if the add-on contains one of the words, otherwise false. + */ + bool Server::filterByWords(const core::stringw words) const + { + if (words == NULL || words.empty()) + return true; + + std::vector list = StringUtils::split(words, ' ', false); + + for (unsigned int i = 0; i < list.size(); i++) + { + list[i].make_lower(); + + if ((core::stringw(m_name).make_lower()).find(list[i].c_str()) != -1) + { + return true; + } + } + + return false; + } // filterByWords +} // namespace Online diff --git a/src/online/server.hpp b/src/online/server.hpp new file mode 100644 index 000000000..779e6a8ce --- /dev/null +++ b/src/online/server.hpp @@ -0,0 +1,137 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_SERVER_HPP +#define HEADER_SERVER_HPP + +/** + * \defgroup onlinegroup Online + * Represents a server that is joinable + */ + +#include +#include +#include "io/xml_node.hpp" +#include "utils/types.hpp" + +class XMLNode; + +namespace Online{ + /** + * \ingroup online + */ + class Server + { + public: + + /** Set the sort order used in the comparison function. */ + enum SortOrder { SO_SCORE = 1, // Sorted on satisfaction score + SO_NAME = 2, // Sorted alphabetically by name + SO_PLAYERS = 4 + }; + + protected: + /** The name to be displayed. */ + irr::core::stringw m_name; + std::string m_lower_case_name; //Used for comparison + + uint32_t m_server_id; + uint32_t m_host_id; + + int m_max_players; + + int m_current_players; + + float m_satisfaction_score; + + /** The sort order to be used in the comparison. */ + static SortOrder m_sort_order; + + Server() {}; + + public: + + /** Initialises the object from an XML node. */ + Server(const XMLNode & xml); + // ------------------------------------------------------------------------ + /** Sets the sort order used in the comparison function. It is static, so + * that each instance can access the sort order. */ + static void setSortOrder(SortOrder so) { m_sort_order = so; } + // ------------------------------------------------------------------------ + /** Returns the name of the server. */ + const irr::core::stringw& getName() const { return m_name; } + const std::string & getLowerCaseName() const { return m_lower_case_name; } + // ------------------------------------------------------------------------ + const float getScore() const { return m_satisfaction_score; } + // ------------------------------------------------------------------------ + /** Returns the ID of this server. */ + const uint32_t getServerId() const { return m_server_id; } + const uint32_t getHostId() const { return m_host_id; } + const int getMaxPlayers() const { return m_max_players; } + const int getCurrentPlayers() const { return m_current_players; } + // ------------------------------------------------------------------------ + bool filterByWords(const irr::core::stringw words) const; + // ------------------------------------------------------------------------ + + /** Compares two servers according to the sort order currently defined. + * \param a The addon to compare this addon to. + */ + bool operator<(const Server &server) const + { + switch(m_sort_order) + { + case SO_SCORE: + return m_satisfaction_score < server.getScore(); + break; + case SO_NAME: + // m_id is the lower case name + return m_name < server.getName(); + break; + case SO_PLAYERS: + return m_current_players < server.getCurrentPlayers(); + break; + } // switch + return true; + } // operator< + + // ------------------------------------------------------------------------ + /** Compares two addons according to the sort order currently defined. + * Comparison is done for sorting in descending order. + * \param a The addon to compare this addon to. + */ + bool operator>(const Server &server) const + { + switch(m_sort_order) + { + case SO_SCORE: + return m_satisfaction_score > server.getScore(); + break; + case SO_NAME: + return m_lower_case_name > server.getLowerCaseName(); + break; + case SO_PLAYERS: + return m_current_players > server.getCurrentPlayers(); + break; + } // switch + return true; + } // operator> + + }; // Server +} // namespace Online + +#endif diff --git a/src/online/servers_manager.cpp b/src/online/servers_manager.cpp new file mode 100644 index 000000000..6184a91b4 --- /dev/null +++ b/src/online/servers_manager.cpp @@ -0,0 +1,176 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "online/servers_manager.hpp" + +#include +#include +#include +#include "config/user_config.hpp" +#include "utils/translation.hpp" +#include "utils/time.hpp" + +#define SERVER_REFRESH_INTERVAL 5.0f + +namespace Online{ + + static ServersManager* manager_singleton(NULL); + + ServersManager* ServersManager::get() + { + if (manager_singleton == NULL) + manager_singleton = new ServersManager(); + return manager_singleton; + } + + void ServersManager::deallocate() + { + delete manager_singleton; + manager_singleton = NULL; + } // deallocate + + // ============================================================================ + ServersManager::ServersManager(){ + m_last_load_time.setAtomic(0.0f); + m_joined_server.setAtomic(NULL); + } + + ServersManager::~ServersManager(){ + cleanUpServers(); + MutexLocker(m_joined_server); + delete m_joined_server.getData(); + } + + // ============================================================================ + void ServersManager::cleanUpServers() + { + m_sorted_servers.lock(); + m_sorted_servers.getData().clearAndDeleteAll(); + m_sorted_servers.unlock(); + m_mapped_servers.lock(); + m_mapped_servers.getData().clear(); + m_mapped_servers.unlock(); + } + + // ============================================================================ + ServersManager::RefreshRequest * ServersManager::refreshRequest(bool request_now) const + { + RefreshRequest * request = NULL; + if(StkTime::getRealTime() - m_last_load_time.getAtomic() > SERVER_REFRESH_INTERVAL) + { + request = new RefreshRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); + request->setParameter("action",std::string("get_server_list")); + if (request_now) + HTTPManager::get()->addRequest(request); + } + return request; + } + + void ServersManager::refresh(bool success, const XMLNode * input) + { + if (success) + { + const XMLNode * servers_xml = input->getNode("servers"); + cleanUpServers(); + for (unsigned int i = 0; i < servers_xml->getNumNodes(); i++) + { + addServer(new Server(*servers_xml->getNode(i))); + } + m_last_load_time.setAtomic((float)StkTime::getRealTime()); + } + //FIXME error message + } + + void ServersManager::RefreshRequest::callback() + { + ServersManager::get()->refresh(m_success, m_result); + } + + // ============================================================================ + const Server * ServersManager::getQuickPlay() const + { + if(m_sorted_servers.getData().size() > 0) + return getServerBySort(0); + + return NULL; + } + + // ============================================================================ + void ServersManager::setJoinedServer(uint32_t id) + { + MutexLocker(m_joined_server); + delete m_joined_server.getData(); + //It's a copy! + m_joined_server.getData() = new Server(*getServerByID(id)); + } + + // ============================================================================ + void ServersManager::unsetJoinedServer() + { + MutexLocker(m_joined_server); + delete m_joined_server.getData(); + m_joined_server.getData() = NULL; + } + + // ============================================================================ + void ServersManager::addServer(Server * server) + { + m_sorted_servers.lock(); + m_sorted_servers.getData().push_back(server); + m_sorted_servers.unlock(); + m_mapped_servers.lock(); + m_mapped_servers.getData()[server->getServerId()] = server; + m_mapped_servers.unlock(); + } + + // ============================================================================ + int ServersManager::getNumServers () const + { + MutexLocker(m_sorted_servers); + return m_sorted_servers.getData().size(); + } + + // ============================================================================ + const Server * ServersManager::getServerBySort (int index) const + { + MutexLocker(m_sorted_servers); + return m_sorted_servers.getData().get(index); + } + + // ============================================================================ + const Server * ServersManager::getServerByID (uint32_t id) const + { + MutexLocker(m_mapped_servers); + return m_mapped_servers.getData().at(id); + } + + // ============================================================================ + Server * ServersManager::getJoinedServer() const + { + return m_joined_server.getAtomic(); + } + + // ============================================================================ + void ServersManager::sort(bool sort_desc){ + MutexLocker(m_sorted_servers); + m_sorted_servers.getData().insertionSort(0, sort_desc); + } + +} // namespace Online diff --git a/src/online/servers_manager.hpp b/src/online/servers_manager.hpp new file mode 100644 index 000000000..808ae6b1b --- /dev/null +++ b/src/online/servers_manager.hpp @@ -0,0 +1,86 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_SERVERS_MANAGER_HPP +#define HEADER_SERVERS_MANAGER_HPP + +#include "utils/ptr_vector.hpp" +#include "utils/types.hpp" +#include "online/server.hpp" +#include "http_manager.hpp" +#include "utils/synchronised.hpp" + + + + +namespace Online { + + /** + * \brief + * \ingroup online + */ + class ServersManager + { + public: + + class RefreshRequest : public XMLRequest + { + virtual void callback (); + public: + RefreshRequest() : XMLRequest() {} + }; + + private: + ServersManager(); + ~ServersManager(); + /** Sorted vector of servers */ + Synchronised > m_sorted_servers; + /** Maps server id's to the same servers*/ + Synchronised > m_mapped_servers; + /** This is a pointer to a copy of the server, the moment it got joined */ + Synchronised m_joined_server; + + Synchronised m_last_load_time; + void refresh(bool success, const XMLNode * input); + void cleanUpServers(); + + public: + // Singleton + static ServersManager* get(); + static void deallocate(); + + RefreshRequest * refreshRequest(bool request_now = true) const; + void setJoinedServer(uint32_t server_id); + void unsetJoinedServer(); + void addServer(Server * server); + int getNumServers () const; + const Server * getServerByID (uint32_t server_id) const; + const Server * getServerBySort (int index) const; + void sort(bool sort_desc); + Server * getJoinedServer() const; + //Returns the best server to join + const Server * getQuickPlay() const; + }; // class ServersManager + + +} // namespace Online + + +#endif + +/*EOF*/ diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 6cbefbd75..2647c8756 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * Copyright (C) 2005-2013 Erwin Coumans http://continuousphysics.com/Bullet/ * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, @@ -84,6 +84,7 @@ btWheelInfo& btKart::addWheel(const btVector3& connectionPointCS, ci.m_maxSuspensionForce = tuning.m_maxSuspensionForce; m_wheelInfo.push_back( btWheelInfo(ci)); + m_visual_contact_point.push_back(btVector3()); btWheelInfo& wheel = m_wheelInfo[getNumWheels()-1]; @@ -111,16 +112,18 @@ void btKart::reset() wheel.m_rotation = 0; updateWheelTransform(i, true); } - m_zipper_active = false; - m_zipper_velocity = btScalar(0); - m_skid_angular_velocity = 0; - m_is_skidding = false; - m_allow_sliding = false; - m_num_wheels_on_ground = 0; - m_additional_impulse = btVector3(0,0,0); - m_time_additional_impulse = 0; - m_additional_rotation = btVector3(0,0,0); - m_time_additional_rotation = 0; + m_visual_wheels_touch_ground = false; + m_zipper_active = false; + m_zipper_velocity = btScalar(0); + m_skid_angular_velocity = 0; + m_is_skidding = false; + m_allow_sliding = false; + m_num_wheels_on_ground = 0; + m_additional_impulse = btVector3(0,0,0); + m_time_additional_impulse = 0; + m_additional_rotation = btVector3(0,0,0); + m_time_additional_rotation = 0; + m_visual_rotation = 0; // Set the brakes so that karts don't slide downhill setAllBrakes(5.0f); @@ -209,8 +212,12 @@ void btKart::updateWheelTransformsWS(btWheelInfo& wheel, } // updateWheelTransformsWS // ---------------------------------------------------------------------------- -btScalar btKart::rayCast(btWheelInfo& wheel) +/** + */ +btScalar btKart::rayCast(unsigned int index) { + btWheelInfo &wheel = m_wheelInfo[index]; + // Work around a bullet problem: when using a convex hull the raycast // would sometimes hit the chassis (which does not happen when using a // box shape). Therefore set the collision mask in the chassis body so @@ -225,7 +232,6 @@ btScalar btKart::rayCast(btWheelInfo& wheel) updateWheelTransformsWS( wheel,false); - btScalar depth = -1; btScalar raylen = wheel.getSuspensionRestLength()+wheel.m_wheelsRadius @@ -311,12 +317,53 @@ btScalar btKart::rayCast(btWheelInfo& wheel) wheel.m_clippedInvContactDotSuspension = btScalar(1.0); } +#define USE_VISUAL +#ifndef USE_VISUAL + m_visual_contact_point[index] = rayResults.m_hitPointInWorld; +#else + if(index==2 || index==3) + { + if(m_visual_rotation==0.123123123) + { + m_visual_contact_point[index ] = rayResults.m_hitPointInWorld; + m_visual_contact_point[index-2] = source; + m_visual_wheels_touch_ground &= (object!=NULL); + } + else + { + btTransform chassisTrans = getChassisWorldTransform(); + if (getRigidBody()->getMotionState()) + { + getRigidBody()->getMotionState()->getWorldTransform(chassisTrans); + } + btQuaternion q(m_visual_rotation, 0, 0); + btQuaternion rot_new = chassisTrans.getRotation() * q; + chassisTrans.setRotation(rot_new); + btVector3 pos = m_kart->getKartModel()->getWheelGraphicsPosition(index); + pos.setZ(pos.getZ()*0.9f); + //pos.setX(pos.getX()*0.1f); + //btVector3 pos = wheel.m_chassisConnectionPointCS; + btVector3 source = chassisTrans( pos ); + btVector3 target = source + rayvector; + btVehicleRaycaster::btVehicleRaycasterResult rayResults; + + void* object = m_vehicleRaycaster->castRay(source,target,rayResults); + m_visual_contact_point[index] = rayResults.m_hitPointInWorld; + m_visual_contact_point[index-2] = source; + m_visual_wheels_touch_ground &= (object!=NULL); + } + } +#endif + if(m_chassisBody->getBroadphaseHandle()) { m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup = old_group; } + + return depth; + } // rayCast // ---------------------------------------------------------------------------- @@ -342,11 +389,12 @@ void btKart::updateVehicle( btScalar step ) // Simulate suspension // ------------------- - m_num_wheels_on_ground = 0; + m_num_wheels_on_ground = 0; + m_visual_wheels_touch_ground = true; for (int i=0;idrawLine(m_visual_contact_point[0], m_visual_contact_point[2], yellow); + debugDrawer->drawLine(m_visual_contact_point[1], m_visual_contact_point[3], yellow); } // debugDraw diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index 6f1945fb7..268836a4a 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * Copyright (C) 2005-2013 Erwin Coumans http://continuousphysics.com/Bullet/ * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, @@ -122,6 +122,17 @@ private: * properties. */ Kart *m_kart; + /** A visual rotation applied to the kart (for skidding). + * The physics use this to provide proper wheel contact points + * for skid marks. */ + float m_visual_rotation; + + /** True if the visual wheels touch the ground. */ + bool m_visual_wheels_touch_ground; + + /** Contact point of the visual wheel position. */ + btAlignedObjectArray m_visual_contact_point; + btAlignedObjectArray m_wheelInfo; void defaultInit(); @@ -142,7 +153,7 @@ public: void reset(); void debugDraw(btIDebugDraw* debugDrawer); const btTransform& getChassisWorldTransform() const; - btScalar rayCast(btWheelInfo& wheel); + btScalar rayCast(unsigned int index); virtual void updateVehicle(btScalar step); void resetSuspension(); btScalar getSteeringValue(int wheel) const; @@ -169,7 +180,22 @@ public: void setSliding(bool active); void instantSpeedIncreaseTo(float speed); void capSpeed(float max_speed); - + // ------------------------------------------------------------------------ + /** Returns true if both rear visual wheels touch the ground. */ + bool visualWheelsTouchGround() const + { + return m_visual_wheels_touch_ground; + } // visualWheelsTouchGround + // ------------------------------------------------------------------------ + /** Returns the contact point of a visual wheel. + * \param n Index of the wheel, must be 2 or 3 since only the two rear + * wheels define the visual position + */ + const btVector3& getVisualContactPoint(int n) const + { + assert(n>=2 && n<=3); + return m_visual_contact_point[n]; + } // getVisualContactPoint // ------------------------------------------------------------------------ /** btActionInterface interface. */ virtual void updateAction(btCollisionWorld* collisionWorld, @@ -230,6 +256,14 @@ public: /** Returns the time an additional impulse is activated. */ float getCentralImpulseTime() const { return m_time_additional_impulse; } // ------------------------------------------------------------------------ + /** Sets a visual rotation to be applied, which the physics use to provide + * the location where the graphical wheels touch the ground (for + * skidmarks). */ + void setVisualRotation(float angle) + { + m_visual_rotation = angle; + } // setVisualRotation + // ------------------------------------------------------------------------ /** Sets a rotation that is applied over a certain amount of time (to avoid * a too rapid changes in the kart). * \param t Time for the rotation to be applied. diff --git a/src/physics/btKartRaycast.cpp b/src/physics/btKartRaycast.cpp index 9fee3075a..13b310f76 100644 --- a/src/physics/btKartRaycast.cpp +++ b/src/physics/btKartRaycast.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * Copyright (C) 2005-2013 Erwin Coumans http://continuousphysics.com/Bullet/ * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, @@ -37,9 +37,9 @@ void* btKartRaycaster::castRay(const btVector3& from, const btVector3& to, } // CloestWithNormal // -------------------------------------------------------------------- /** Stores the index of the triangle hit. */ - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) - { + { // We don't always get a triangle index, sometimes (e.g. ray hits // other kart) we get shapePart=-1, or no localShapeInfo at all if(rayResult.m_localShapeInfo && @@ -48,7 +48,7 @@ void* btKartRaycaster::castRay(const btVector3& from, const btVector3& to, return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); - } + } // -------------------------------------------------------------------- /** Returns the index of the triangle which was hit, or -1 if * no triangle was hit. */ @@ -57,19 +57,19 @@ void* btKartRaycaster::castRay(const btVector3& from, const btVector3& to, }; // CloestWithNormal // ======================================================================== - ClosestWithNormal rayCallback(from,to); + ClosestWithNormal rayCallback(from,to); - m_dynamicsWorld->rayTest(from, to, rayCallback); + m_dynamicsWorld->rayTest(from, to, rayCallback); - if (rayCallback.hasHit()) - { - btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject); + if (rayCallback.hasHit()) + { + btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject); if (body && body->hasContactResponse()) - { - result.m_hitPointInWorld = rayCallback.m_hitPointWorld; - result.m_hitNormalInWorld = rayCallback.m_hitNormalWorld; - result.m_hitNormalInWorld.normalize(); - result.m_distFraction = rayCallback.m_closestHitFraction; + { + result.m_hitPointInWorld = rayCallback.m_hitPointWorld; + result.m_hitNormalInWorld = rayCallback.m_hitNormalWorld; + result.m_hitNormalInWorld.normalize(); + result.m_distFraction = rayCallback.m_closestHitFraction; const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh(); if(m_smooth_normals && @@ -89,8 +89,8 @@ void* btKartRaycaster::castRay(const btVector3& from, const btVector3& to, #endif } return body; - } - } - return 0; + } + } + return 0; } diff --git a/src/physics/btKartRaycast.hpp b/src/physics/btKartRaycast.hpp index 624d2012e..f9c7faa16 100644 --- a/src/physics/btKartRaycast.hpp +++ b/src/physics/btKartRaycast.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * Copyright (C) 2005-2013 Erwin Coumans http://continuousphysics.com/Bullet/ * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, @@ -23,17 +23,17 @@ class btDynamicsWorld; class btKartRaycaster : public btVehicleRaycaster { private: - btDynamicsWorld* m_dynamicsWorld; + btDynamicsWorld* m_dynamicsWorld; /** True if the normals should be smoothed. Not all tracks support this, * so this flag is set depending on track when constructing this object. */ bool m_smooth_normals; public: - btKartRaycaster(btDynamicsWorld* world, bool smooth_normals=false) - :m_dynamicsWorld(world), m_smooth_normals(smooth_normals) - { - } + btKartRaycaster(btDynamicsWorld* world, bool smooth_normals=false) + :m_dynamicsWorld(world), m_smooth_normals(smooth_normals) + { + } - virtual void* castRay(const btVector3& from,const btVector3& to, + virtual void* castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result); }; diff --git a/src/physics/btUprightConstraint.cpp b/src/physics/btUprightConstraint.cpp index 5221207f8..3b183c5e6 100644 --- a/src/physics/btUprightConstraint.cpp +++ b/src/physics/btUprightConstraint.cpp @@ -1,7 +1,7 @@ /* Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ +Copyright (C) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the diff --git a/src/physics/btUprightConstraint.hpp b/src/physics/btUprightConstraint.hpp index 4fc425faf..a2711fa9f 100644 --- a/src/physics/btUprightConstraint.hpp +++ b/src/physics/btUprightConstraint.hpp @@ -1,7 +1,7 @@ /* Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ +Copyright (C) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the @@ -111,7 +111,7 @@ public: timeStep); virtual void getInfo1 (btConstraintInfo1* info); virtual void getInfo2 (btConstraintInfo2* info); - virtual void setParam(int num, btScalar value, int axis = -1); + virtual void setParam(int num, btScalar value, int axis = -1); virtual btScalar getParam(int num, int axis) const; }; diff --git a/src/physics/irr_debug_drawer.cpp b/src/physics/irr_debug_drawer.cpp index 4937ad284..7f82ffa7c 100644 --- a/src/physics/irr_debug_drawer.cpp +++ b/src/physics/irr_debug_drawer.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/physics/irr_debug_drawer.hpp b/src/physics/irr_debug_drawer.hpp index 00eddcdbf..3b11f217f 100644 --- a/src/physics/irr_debug_drawer.hpp +++ b/src/physics/irr_debug_drawer.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/physics/kart_motion_state.hpp b/src/physics/kart_motion_state.hpp index 0fcc89b0c..8e0fa80e0 100644 --- a/src/physics/kart_motion_state.hpp +++ b/src/physics/kart_motion_state.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,11 +19,8 @@ #ifndef HEADER_KART_MOTION_STATE_HPP #define HEADER_KART_MOTION_STATE_HPP -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include +#include "utils/vs.hpp" #include "LinearMath/btMotionState.h" diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index 0d75b9a82..dee4f40bb 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -38,48 +38,71 @@ using namespace irr; #include #include -// ---------------------------------------------------------------------------- +/** Creates a physical Settings object with the given type, radius and mass. + */ +PhysicalObject::Settings::Settings(BodyTypes type, float radius, float mass) +{ + init(); + m_body_type = type; + m_mass = mass; + m_radius = radius; +} // Settings +// ---------------------------------------------------------------------------- +/** Reads the physical settings values from a given XML node. + */ +PhysicalObject::Settings::Settings(const XMLNode &xml_node) +{ + init(); + std::string shape; + xml_node.get("mass", &m_mass ); + xml_node.get("radius", &m_radius ); + xml_node.get("shape", &shape ); + xml_node.get("reset", &m_crash_reset ); + xml_node.get("explode", &m_knock_kart ); + xml_node.get("flatten", &m_flatten_kart); + + m_reset_when_too_low = + xml_node.get("reset-when-below", &m_reset_height) == 1; + + m_body_type = MP_NONE; + if (shape=="cone" || + shape=="coneY" ) m_body_type = MP_CONE_Y; + else if(shape=="coneX" ) m_body_type = MP_CONE_X; + else if(shape=="coneZ" ) m_body_type = MP_CONE_Z; + else if(shape=="cylinder"|| + shape=="cylinderY") m_body_type = MP_CYLINDER_Y; + else if(shape=="cylinderX") m_body_type = MP_CYLINDER_X; + else if(shape=="cylinderZ") m_body_type = MP_CYLINDER_Z; + else if(shape=="box" ) m_body_type = MP_BOX; + else if(shape=="sphere" ) m_body_type = MP_SPHERE; + else if(shape=="exact" ) m_body_type = MP_EXACT; + + else + Log::error("PhysicalObject", "Unknown shape type : %s.", + shape.c_str()); +} // Settings(XMLNode) + +// ---------------------------------------------------------------------------- +/** Initialises a Settings object. + */ +void PhysicalObject::Settings::init() +{ + m_body_type = PhysicalObject::MP_NONE; + m_crash_reset = false; + m_knock_kart = false; + m_mass = -1.0f; + m_radius = -1.0f; + m_reset_when_too_low = false; + m_flatten_kart = false; +} // Settings + +// ============================================================================ PhysicalObject* PhysicalObject::fromXML(bool is_dynamic, const XMLNode &xml_node, TrackObject* object) { - PhysicalObject::Settings settings; - - settings.reset_height = 0; - settings.mass = 1; - settings.radius = -1; - settings.crash_reset = false; - settings.knock_kart = false; - settings.flatten_kart = false; - - std::string shape; - xml_node.get("mass", &settings.mass ); - xml_node.get("radius", &settings.radius ); - xml_node.get("shape", &shape ); - xml_node.get("reset", &settings.crash_reset ); - xml_node.get("explode", &settings.knock_kart ); - xml_node.get("flatten", &settings.flatten_kart); - - settings.reset_when_too_low = - xml_node.get("reset-when-below", &settings.reset_height) == 1; - - settings.body_type = MP_NONE; - if (shape=="cone" || - shape=="coneY" ) settings.body_type = MP_CONE_Y; - else if(shape=="coneX" ) settings.body_type = MP_CONE_X; - else if(shape=="coneZ" ) settings.body_type = MP_CONE_Z; - else if(shape=="cylinder"|| - shape=="cylinderY") settings.body_type = MP_CYLINDER_Y; - else if(shape=="cylinderX") settings.body_type = MP_CYLINDER_X; - else if(shape=="cylinderZ") settings.body_type = MP_CYLINDER_Z; - - else if(shape=="box" ) settings.body_type = MP_BOX; - else if(shape=="sphere" ) settings.body_type = MP_SPHERE; - else if(shape=="exact") settings.body_type = MP_EXACT; - - else fprintf(stderr, "Unknown shape type : %s\n", shape.c_str()); - + PhysicalObject::Settings settings(xml_node); return new PhysicalObject(is_dynamic, settings, object); } // fromXML @@ -107,14 +130,14 @@ PhysicalObject::PhysicalObject(bool is_dynamic, m_init_hpr = object->getRotation(); m_init_scale = object->getScale(); - m_mass = settings.mass; - m_radius = settings.radius; - m_body_type = settings.body_type; - m_crash_reset = settings.crash_reset; - m_explode_kart = settings.knock_kart; - m_flatten_kart = settings.flatten_kart; - m_reset_when_too_low = settings.reset_when_too_low; - m_reset_height = settings.reset_height; + m_mass = settings.m_mass; + m_radius = settings.m_radius; + m_body_type = settings.m_body_type; + m_crash_reset = settings.m_crash_reset; + m_explode_kart = settings.m_knock_kart; + m_flatten_kart = settings.m_flatten_kart; + m_reset_when_too_low = settings.m_reset_when_too_low; + m_reset_height = settings.m_reset_height; m_init_pos.setIdentity(); Vec3 radHpr(m_init_hpr); @@ -529,10 +552,28 @@ void PhysicalObject::handleExplosion(const Vec3& pos, bool direct_hit) } // handleExplosion // ---------------------------------------------------------------------------- -bool PhysicalObject::isSoccerBall() +/** Returns true if this object is a soccer ball. + */ +bool PhysicalObject::isSoccerBall() const { - return m_object->isSoccerBall(); -} + return m_object->isSoccerBall(); +} // is SoccerBall + +// ---------------------------------------------------------------------------- +/** Called when a physical object hits the track. Atm only used to push a + * soccer ball away from the edge of the field. + * \param m Material which was hit. + * \param normal Normal of the track at the hit point. + */ +void PhysicalObject::hit(const Material *m, const Vec3 &normal) +{ + if(isSoccerBall() && m != NULL && + m->getCollisionReaction() == Material::PUSH_SOCCER_BALL) + { + m_body->applyCentralImpulse(normal * 1000.0f); + } +} // hit + // ---------------------------------------------------------------------------- /* EOF */ diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index 6f2deb4a0..0cb52df59 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,8 +28,9 @@ #include "utils/leak_check.hpp" -class XMLNode; +class Material; class TrackObject; +class XMLNode; /** * \ingroup physics @@ -38,22 +39,38 @@ class PhysicalObject { public: /** The supported collision shapes. */ - enum bodyTypes {MP_NONE, + enum BodyTypes {MP_NONE, MP_CONE_Y, MP_CONE_X, MP_CONE_Z, MP_CYLINDER_Y, MP_CYLINDER_X, MP_CYLINDER_Z, MP_BOX, MP_SPHERE, MP_EXACT}; - struct Settings + class Settings { - float mass; - float radius; - PhysicalObject::bodyTypes body_type; - bool crash_reset; - bool knock_kart; - bool flatten_kart; - bool reset_when_too_low; - float reset_height; - }; + public: + /** Mass of the object. */ + float m_mass; + /** Radius of the object. */ + float m_radius; + /** Shape of the object. */ + PhysicalObject::BodyTypes m_body_type; + /** Trigger a reset in karts touching it? */ + bool m_crash_reset; + /** Knock the kart around. */ + bool m_knock_kart; + /** Flatten the kart when this object is touched. */ + bool m_flatten_kart; + /** Reset the object when it falls under the track (useful + * e.g. for a boulder rolling down a hill). */ + bool m_reset_when_too_low; + /** If the item is below that height, it is reset (when + * m_reset_when_too_low is true). */ + float m_reset_height; + private: + void init(); + public: + Settings(BodyTypes type, float radius, float mass); + Settings(const XMLNode &xml_node); + }; // Settings private: @@ -69,7 +86,7 @@ private: TrackObject *m_object; /** The shape of this object. */ - bodyTypes m_body_type; + BodyTypes m_body_type; /** The bullet collision shape. */ btCollisionShape *m_shape; @@ -139,8 +156,9 @@ public: virtual void handleExplosion(const Vec3& pos, bool directHit); void update (float dt); void init (); - bool isSoccerBall(); - + void move (const Vec3& xyz, const core::vector3df& hpr); + void hit (const Material *m, const Vec3 &normal); + bool isSoccerBall () const; // ------------------------------------------------------------------------ /** Returns the rigid body of this physical object. */ btRigidBody *getBody () { return m_body; } @@ -148,10 +166,15 @@ public: /** Returns true if this object should trigger a rescue in a kart that * hits it. */ bool isCrashReset() const { return m_crash_reset; } + // ------------------------------------------------------------------------ + /** Returns true if this object should cause an explosion if a kart hits + * it. */ bool isExplodeKartObject () const { return m_explode_kart; } + // ------------------------------------------------------------------------ + /** Returns true if this object should cause a kart that touches it to + * be flattened. */ bool isFlattenKartObject () const { return m_flatten_kart; } - void move(const Vec3& xyz, const core::vector3df& hpr); LEAK_CHECK() }; // PhysicalObject diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 0b955f97a..dd679b18e 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -8,7 +8,7 @@ // 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 ofati +// 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. // @@ -18,12 +18,16 @@ #include "physics/physics.hpp" +#include "achievements/achievements_manager.hpp" #include "animations/three_d_animation.hpp" +#include "karts/abstract_kart.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/stars.hpp" +#include "items/flyable.hpp" #include "karts/kart_properties.hpp" #include "karts/rescue_animation.hpp" -#include "network/race_state.hpp" -#include "graphics/stars.hpp" +#include "modes/soccer_world.hpp" +#include "modes/world.hpp" #include "karts/explosion_animation.hpp" #include "physics/btKart.hpp" #include "physics/btUprightConstraint.hpp" @@ -32,7 +36,6 @@ #include "physics/stk_dynamics_world.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" -#include "modes/soccer_world.hpp" // ---------------------------------------------------------------------------- /** Initialise physics. @@ -85,13 +88,13 @@ Physics::~Physics() */ void Physics::addKart(const AbstractKart *kart) { - const btCollisionObjectArray &all_objs = - m_dynamics_world->getCollisionObjectArray(); - for(unsigned int i=0; i<(unsigned int)all_objs.size(); i++) - { - if(btRigidBody::upcast(all_objs[i])== kart->getBody()) - return; - } + const btCollisionObjectArray &all_objs = + m_dynamics_world->getCollisionObjectArray(); + for(unsigned int i=0; i<(unsigned int)all_objs.size(); i++) + { + if(btRigidBody::upcast(all_objs[i])== kart->getBody()) + return; + } m_dynamics_world->addRigidBody(kart->getBody()); m_dynamics_world->addVehicle(kart->getVehicle()); m_dynamics_world->addConstraint(kart->getUprightConstraint()); @@ -158,8 +161,6 @@ void Physics::update(float dt) { AbstractKart *a=p->getUserPointer(0)->getPointerKart(); AbstractKart *b=p->getUserPointer(1)->getPointerKart(); - race_state->addCollision(a->getWorldKartId(), - b->getWorldKartId()); KartKartCollision(p->getUserPointer(0)->getPointerKart(), p->getContactPointCS(0), p->getUserPointer(1)->getPointerKart(), @@ -189,12 +190,12 @@ void Physics::update(float dt) const KartProperties* kp = kart->getKartProperties(); kart->setSquash(kp->getSquashDuration(), kp->getSquashSlowdown()); } - else if(obj->isSoccerBall()) + else if(obj->isSoccerBall()) { - int kartId = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); - SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); - soccerWorld->setLastKartTohitBall(kartId); - } + int kartId = p->getUserPointer(1)->getPointerKart()->getWorldKartId(); + SoccerWorld* soccerWorld = (SoccerWorld*)World::getWorld(); + soccerWorld->setLastKartTohitBall(kartId); + } continue; } @@ -251,12 +252,12 @@ void Physics::update(float dt) // Only explode a bowling ball if the target is // not invulnerable AbstractKart* target_kart = p->getUserPointer(1)->getPointerKart(); - if(p->getUserPointer(0)->getPointerFlyable()->getType() - !=PowerupManager::POWERUP_BOWLING || - !target_kart->isInvulnerable() ) + PowerupManager::PowerupType type = p->getUserPointer(0)->getPointerFlyable()->getType(); + if(type != PowerupManager::POWERUP_BOWLING || !target_kart->isInvulnerable()) { - p->getUserPointer(0)->getPointerFlyable() - ->hit(target_kart); + p->getUserPointer(0)->getPointerFlyable()->hit(target_kart); + if ( type ==PowerupManager::POWERUP_BOWLING ) + ((SingleAchievement *) AchievementsManager::get()->getActive()->getAchievement(2))->increase(1); } } @@ -474,7 +475,6 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, else if(upB->is(UserPointer::UP_KART)) { AbstractKart *kart=upB->getPointerKart(); - race_state->addCollision(kart->getWorldKartId()); int n = contact_manifold->getContactPoint(0).m_index0; const Material *m = n>=0 ? upA->getPointerTriangleMesh()->getMaterial(n) @@ -486,6 +486,16 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, .m_normalWorldOnB; kart->crashed(m, normal); } + else if(upB->is(UserPointer::UP_PHYSICAL_OBJECT)) + { + int n = contact_manifold->getContactPoint(0).m_index1; + const Material *m + = n>=0 ? upA->getPointerTriangleMesh()->getMaterial(n) + : NULL; + const btVector3 &normal = contact_manifold->getContactPoint(0) + .m_normalWorldOnB; + upB->getPointerPhysicalObject()->hit(m, normal); + } } // 2) object a is a kart // ===================== @@ -494,7 +504,6 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, if(upB->is(UserPointer::UP_TRACK)) { AbstractKart *kart = upA->getPointerKart(); - race_state->addCollision(kart->getWorldKartId()); int n = contact_manifold->getContactPoint(0).m_index1; const Material *m = n>=0 ? upB->getPointerTriangleMesh()->getMaterial(n) @@ -553,6 +562,16 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, m_all_collisions.push_back( upA, contact_manifold->getContactPoint(0).m_localPointA, upB, contact_manifold->getContactPoint(0).m_localPointB); + else if(upB->is(UserPointer::UP_TRACK)) + { + int n = contact_manifold->getContactPoint(0).m_index1; + const Material *m + = n>=0 ? upB->getPointerTriangleMesh()->getMaterial(n) + : NULL; + const btVector3 &normal = contact_manifold->getContactPoint(0) + .m_normalWorldOnB; + upA->getPointerPhysicalObject()->hit(m, normal); + } } else if (upA->is(UserPointer::UP_ANIMATION)) { diff --git a/src/physics/physics.hpp b/src/physics/physics.hpp index 9b160aed0..d933c18c2 100644 --- a/src/physics/physics.hpp +++ b/src/physics/physics.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/physics/stk_dynamics_world.hpp b/src/physics/stk_dynamics_world.hpp index 5be3db306..d0f2f49d0 100644 --- a/src/physics/stk_dynamics_world.hpp +++ b/src/physics/stk_dynamics_world.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,7 +24,7 @@ class STKDynamicsWorld : public btDiscreteDynamicsWorld { public: - /** The standard constructor which just created a btDiscreteDynamicsWorld. */ + /** The standard constructor which just created a btDiscreteDynamicsWorld. */ STKDynamicsWorld(btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btConstraintSolver* constraintSolver, diff --git a/src/physics/triangle_mesh.cpp b/src/physics/triangle_mesh.cpp index 4b61aea92..83f79dfa4 100644 --- a/src/physics/triangle_mesh.cpp +++ b/src/physics/triangle_mesh.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/physics/triangle_mesh.hpp b/src/physics/triangle_mesh.hpp index 9a2978ef5..118ec2de5 100644 --- a/src/physics/triangle_mesh.hpp +++ b/src/physics/triangle_mesh.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/physics/user_pointer.hpp b/src/physics/user_pointer.hpp index fc8615c09..b9f6612df 100644 --- a/src/physics/user_pointer.hpp +++ b/src/physics/user_pointer.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/grand_prix_data.cpp b/src/race/grand_prix_data.cpp index 3c1f18cc9..bcb3d2bec 100644 --- a/src/race/grand_prix_data.cpp +++ b/src/race/grand_prix_data.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Ingo Ruhnke -// Copyright (C) 2006 Joerg Henrichs, Ingo Ruhnke +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp index 2004bef9c..074b6534f 100644 --- a/src/race/grand_prix_data.hpp +++ b/src/race/grand_prix_data.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2006 Ingo Ruhnke -// Copyright (C) 2006 Joerg Henirchs, Ingo Ruhnke +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/grand_prix_manager.cpp b/src/race/grand_prix_manager.cpp index 72400dcea..cd63f0e14 100644 --- a/src/race/grand_prix_manager.cpp +++ b/src/race/grand_prix_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/grand_prix_manager.hpp b/src/race/grand_prix_manager.hpp index 5f305c6b6..2afb979b6 100644 --- a/src/race/grand_prix_manager.hpp +++ b/src/race/grand_prix_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/highscore_manager.cpp b/src/race/highscore_manager.cpp index d4e6b642c..35ed63c1c 100644 --- a/src/race/highscore_manager.cpp +++ b/src/race/highscore_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -59,7 +59,7 @@ void HighscoreManager::setFilename() } else { - m_filename=file_manager->getHighscoreFile("highscore.xml"); + m_filename=file_manager->getConfigFile("highscore.xml"); } return; diff --git a/src/race/highscore_manager.hpp b/src/race/highscore_manager.hpp index 128277bd2..913357245 100644 --- a/src/race/highscore_manager.hpp +++ b/src/race/highscore_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/highscores.cpp b/src/race/highscores.cpp index d23314af1..034ca8a87 100644 --- a/src/race/highscores.cpp +++ b/src/race/highscores.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/highscores.hpp b/src/race/highscores.hpp index 0e31b6904..b713bc7f0 100644 --- a/src/race/highscores.hpp +++ b/src/race/highscores.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/history.cpp b/src/race/history.cpp index b6af7a2ac..435473f44 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/race/history.hpp b/src/race/history.hpp index 22aa430ed..02f0570e3 100644 --- a/src/race/history.hpp +++ b/src/race/history.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -20,6 +20,7 @@ #define HEADER_HISTORY_HPP #include +#include #include "LinearMath/btQuaternion.h" diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index b7247bedf..dfb9229a1 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -42,7 +42,9 @@ #include "modes/world.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/soccer_world.hpp" -#include "network/network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/network_world.hpp" +#include "network/protocols/start_game_protocol.hpp" #include "states_screens/grand_prix_lose.hpp" #include "states_screens/grand_prix_win.hpp" #include "states_screens/kart_selection.hpp" @@ -143,9 +145,10 @@ void RaceManager::setLocalKartInfo(unsigned int player_id, assert(0<=player_id && player_id getKart(kart) != NULL); + const PlayerProfile* profile = StateManager::get()->getActivePlayerProfile(player_id); m_local_player_karts[player_id] = RemoteKartInfo(player_id, kart, - StateManager::get()->getActivePlayerProfile(player_id)->getName(), - network_manager->getMyHostId()); + profile->getName(), + 0, false); } // setLocalKartInfo //----------------------------------------------------------------------------- @@ -293,7 +296,7 @@ void RaceManager::startNew(bool from_overworld) // Create the kart status data structure to keep track of scores, times, ... // ========================================================================== m_kart_status.clear(); - + Log::verbose("RaceManager", "Nb of karts=%u, ai:%lu players:%lu\n", (unsigned int)m_num_karts, m_ai_kart_list.size(), m_player_karts.size()); assert((unsigned int)m_num_karts == m_ai_kart_list.size()+m_player_karts.size()); // First add the AI karts (randomly chosen) @@ -321,8 +324,7 @@ void RaceManager::startNew(bool from_overworld) // ------------------------------------------------- for(int i=m_player_karts.size()-1; i>=0; i--) { - KartType kt=(m_player_karts[i].getHostId()==network_manager->getMyHostId()) - ? KT_PLAYER : KT_NETWORK_PLAYER; + KartType kt= m_player_karts[i].isNetworkPlayer() ? KT_NETWORK_PLAYER : KT_PLAYER; m_kart_status.push_back(KartStatus(m_player_karts[i].getKartName(), i, m_player_karts[i].getLocalPlayerId(), m_player_karts[i].getGlobalPlayerId(), @@ -337,7 +339,7 @@ void RaceManager::startNew(bool from_overworld) } m_track_number = 0; - if(m_major_mode==MAJOR_MODE_GRAND_PRIX) + if(m_major_mode==MAJOR_MODE_GRAND_PRIX && !NetworkWorld::getInstance()->isRunning()) // offline mode only { //We look if Player 1 has a saved version of this GP. // ================================================= @@ -353,8 +355,15 @@ void RaceManager::startNew(bool from_overworld) // ========================================= if(gp != NULL) { - m_track_number = gp->getNextTrack(); - gp->loadKarts(m_kart_status); + if (m_continue_saved_gp) + { + m_track_number = gp->getNextTrack(); + gp->loadKarts(m_kart_status); + } + else + { + gp->remove(); + } } } startNextRace(); @@ -462,6 +471,10 @@ void RaceManager::startNextRace() m_kart_status[i].m_last_time = 0; } + StartGameProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_START_GAME)); + if (protocol) + protocol->ready(); } // startNextRace //----------------------------------------------------------------------------- @@ -474,7 +487,7 @@ void RaceManager::next() m_track_number++; if(m_track_number<(int)m_tracks.size()) { - if(m_major_mode==MAJOR_MODE_GRAND_PRIX) + if(m_major_mode==MAJOR_MODE_GRAND_PRIX && !NetworkWorld::getInstance()->isRunning()) { //Saving GP state //We look if Player 1 has already saved this GP. @@ -509,21 +522,10 @@ void RaceManager::next() } user_config->saveConfig(); } - - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->beginReadySetGoBarrier(); - else - network_manager->setState(NetworkManager::NS_WAIT_FOR_RACE_DATA); startNextRace(); } else { - // Back to main menu. Change the state of the state of the - // network manager. - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->setState(NetworkManager::NS_MAIN_MENU); - else - network_manager->setState(NetworkManager::NS_WAIT_FOR_AVAILABLE_CHARACTERS); exitRace(); } } // next @@ -629,7 +631,7 @@ void RaceManager::exitRace(bool delete_world) if (m_major_mode==MAJOR_MODE_GRAND_PRIX && m_track_number==(int)m_tracks.size()) { unlock_manager->getCurrentSlot()->grandPrixFinished(); - if(m_major_mode==MAJOR_MODE_GRAND_PRIX) + if(m_major_mode==MAJOR_MODE_GRAND_PRIX&& !NetworkWorld::getInstance()->isRunning()) { //Delete saved GP SavedGrandPrix* gp = @@ -728,7 +730,9 @@ void RaceManager::kartFinishedRace(const AbstractKart *kart, float time) m_kart_status[id].m_overall_time += time; m_kart_status[id].m_last_time = time; m_num_finished_karts ++; - if(kart->getController()->isPlayerController()) m_num_finished_players++; + if(kart->getController()->isPlayerController() || + kart->getController()->isNetworkController()) + m_num_finished_players++; } // kartFinishedRace //----------------------------------------------------------------------------- @@ -746,14 +750,16 @@ void RaceManager::rerunRace() //----------------------------------------------------------------------------- -void RaceManager::startGP(const GrandPrixData* gp, bool from_overworld) +void RaceManager::startGP(const GrandPrixData* gp, bool from_overworld, + bool continue_saved_gp) { assert(gp != NULL); StateManager::get()->enterGameState(); setGrandPrix(*gp); setCoinTarget( 0 ); // Might still be set from a previous challenge - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); + m_continue_saved_gp = continue_saved_gp; setMajorMode(RaceManager::MAJOR_MODE_GRAND_PRIX); startNew(from_overworld); @@ -778,9 +784,38 @@ void RaceManager::startSingleRace(const std::string &track_ident, setMajorMode(RaceManager::MAJOR_MODE_SINGLE); setCoinTarget( 0 ); // Might still be set from a previous challenge - network_manager->setupPlayerKartInfo(); + if (!NetworkWorld::getInstance()->isRunning()) // if not in a network world + race_manager->setupPlayerKartInfo(); // do this setup player kart startNew(from_overworld); } +//----------------------------------------------------------------------------- +/** Receive and store the information from sendKartsInformation() +*/ +void RaceManager::setupPlayerKartInfo() +{ + + std::vector kart_info; + + // Get the local kart info + for(unsigned int i=0; i("list_addons"); assert(w_list != NULL); w_list->clearColumns(); - w_list->addColumn( _("Add-on name"), 2 ); + w_list->addColumn( _("Add-on name"), 3 ); w_list->addColumn( _("Updated date"), 1 ); GUIEngine::SpinnerWidget* w_filter_date = @@ -140,9 +140,7 @@ void AddonsScreen::init() m_sort_default = true; m_sort_col = 0; - getWidget("category")->setDeactivated(); - - GUIEngine::getFont()->setTabStop(0.66f); + getWidget("category")->setDeactivated(); if(UserConfigParams::logAddons()) std::cout << "[addons] Using directory <" + file_manager->getAddonsDir() @@ -190,8 +188,6 @@ void AddonsScreen::unloaded() void AddonsScreen::tearDown() { - // return tab stop to the center when leaving this screen!! - GUIEngine::getFont()->setTabStop(0.5f); } // ---------------------------------------------------------------------------- @@ -226,7 +222,7 @@ void AddonsScreen::loadList() PtrVector sorted_list; for(unsigned int i=0; igetNumAddons(); i++) { - const Addon &addon = addons_manager->getAddon(i); + const Addon & addon = addons_manager->getAddon(i); // Ignore addons of a different type if(addon.getType()!=m_type) continue; // Ignore invisible addons @@ -273,18 +269,21 @@ void AddonsScreen::loadList() if(addon->isInstalled()) icon = addon->needsUpdate() ? m_icon_needs_update : m_icon_installed; - else - icon = m_icon_not_installed; + else + icon = m_icon_not_installed; core::stringw s; if (addon->getDesigner().size()==0) s = (addon->getName()+L"\t" + core::stringc(addon->getDateAsString().c_str())).c_str(); - gui::IGUIFont* font = GUIEngine::getFont(); + //FIXME I'd like to move this to CGUISTKListBox.cpp + + /* gui::IGUIFont* font = GUIEngine::getFont(); // first column is 0.666% of the list's width. // and icon width == icon height. + const unsigned int available_width = (int)(w_list->m_w*0.6666f - m_icon_height); if (font->getDimension(s.c_str()).Width > available_width) @@ -293,7 +292,7 @@ void AddonsScreen::loadList() s.append("..."); } else - { + {*/ if (addon->getDesigner().size() == 0) { s = addon->getName(); @@ -304,7 +303,7 @@ void AddonsScreen::loadList() s = _C("addons", "%s by %s", addon->getName().c_str(), addon->getDesigner().c_str()); } - + /* // check if text is too long to fit if (font->getDimension(s.c_str()).Width > available_width) { @@ -344,10 +343,9 @@ void AddonsScreen::loadList() } // for nlines.size() s = final_string; - } // if - s.append("\t"); - s.append(addon->getDateAsString().c_str()); - } + */ + //} // if + //} // we have no icon for featured+updateme, so if an add-on is updatable // forget about the featured icon @@ -357,7 +355,10 @@ void AddonsScreen::loadList() icon += 2; } - w_list->addItem(addon->getId(), s.c_str(), icon); + PtrVector * row = new PtrVector; + row->push_back(new GUIEngine::ListWidget::ListCell(s.c_str(), icon, 3, false)); + row->push_back(new GUIEngine::ListWidget::ListCell(addon->getDateAsString().c_str(), -1, 1, true)); + w_list->addItem(addon->getId(), row); // Highlight if it's not approved in artists debug mode. if(UserConfigParams::m_artist_debug_mode && @@ -367,15 +368,15 @@ void AddonsScreen::loadList() } } - getWidget("category")->setActivated(); - if(m_type == "kart") - getWidget("category")->select("tab_kart", + getWidget("category")->setActivated(); + if(m_type == "kart") + getWidget("category")->select("tab_kart", PLAYER_ID_GAME_MASTER); - else if(m_type == "track") - getWidget("category")->select("tab_track", + else if(m_type == "track") + getWidget("category")->select("tab_track", PLAYER_ID_GAME_MASTER); else - getWidget("category")->select("tab_update", + getWidget("category")->select("tab_update", PLAYER_ID_GAME_MASTER); } // loadList @@ -403,7 +404,7 @@ void AddonsScreen::onColumnClicked(int column_id) case 1: Addon::setSortOrder(m_sort_default ? Addon::SO_DEFAULT : Addon::SO_DATE); break; - default: assert(0); + default: assert(0); break; } // switch /** \brief Toggle the sort order after column click **/ loadList(); @@ -430,9 +431,7 @@ void AddonsScreen::eventCallback(GUIEngine::Widget* widget, w_list->clear(); w_list->addItem("spacer", L""); - w_list->addItem("loading", - _("Please wait while addons are updated"), - m_icon_loading); + w_list->addItem("loading",_("Please wait while addons are updated"), m_icon_loading); } } @@ -445,7 +444,7 @@ void AddonsScreen::eventCallback(GUIEngine::Widget* widget, if (!id.empty() && addons_manager->getAddon(id) != NULL) { m_selected_index = list->getSelectionID(); - new AddonsLoading(0.8f, 0.8f, id); + new AddonsLoading(id); } } if (name == "category") diff --git a/src/states_screens/addons_screen.hpp b/src/states_screens/addons_screen.hpp index a454545b2..b59ac1946 100644 --- a/src/states_screens/addons_screen.hpp +++ b/src/states_screens/addons_screen.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin, Joerg Henrichs +// Copyright (C) 2010-2013 Lucas Baudin, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/arenas_screen.cpp b/src/states_screens/arenas_screen.cpp index 51f435640..8d0cb6f2c 100644 --- a/src/states_screens/arenas_screen.cpp +++ b/src/states_screens/arenas_screen.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/arenas_screen.hpp b/src/states_screens/arenas_screen.hpp index bd64dede3..898538e3e 100644 --- a/src/states_screens/arenas_screen.hpp +++ b/src/states_screens/arenas_screen.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/create_server_screen.cpp b/src/states_screens/create_server_screen.cpp new file mode 100644 index 000000000..f7f50205f --- /dev/null +++ b/src/states_screens/create_server_screen.cpp @@ -0,0 +1,185 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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. + +#define DEBUG_MENU_ITEM 0 + +#include "states_screens/create_server_screen.hpp" + +#include +#include + +#include "challenges/game_slot.hpp" +#include "challenges/unlock_manager.hpp" +#include "audio/sfx_manager.hpp" +#include "states_screens/online_screen.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/dialogs/message_dialog.hpp" +#include "modes/demo_world.hpp" +#include "utils/translation.hpp" +#include "states_screens/networking_lobby.hpp" +#include "states_screens/dialogs/server_info_dialog.hpp" +#include "online/servers_manager.hpp" +#include "online/messages.hpp" + + +using namespace GUIEngine; +using namespace Online; + +DEFINE_SCREEN_SINGLETON( CreateServerScreen ); + +// ---------------------------------------------------------------------------- + +CreateServerScreen::CreateServerScreen() : Screen("online/create_server.stkgui") +{ + m_server_creation_request = NULL; +} // CreateServerScreen + +// ---------------------------------------------------------------------------- + +void CreateServerScreen::loadedFromFile() +{ + + + m_name_widget = getWidget("name"); + assert(m_name_widget != NULL); + m_name_widget->setText(CurrentUser::get()->getUserName() + _("'s server")); + m_max_players_widget = getWidget("max_players"); + assert(m_max_players_widget != NULL); + m_max_players_widget->setValue(8); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_create_widget = getWidget("create"); + assert(m_create_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); + +} // loadedFromFile + +// ---------------------------------------------------------------------------- +void CreateServerScreen::beforeAddingWidget() +{ + +} // beforeAddingWidget + + + +// ---------------------------------------------------------------------------- +void CreateServerScreen::init() +{ + Screen::init(); + setInitialFocus(); + DemoWorld::resetIdleTime(); + m_info_widget->setText("", false); +} +// ---------------------------------------------------------------------------- +void CreateServerScreen::onUpdate(float delta, irr::video::IVideoDriver* driver) +{ + if(m_server_creation_request != NULL) + { + if(m_server_creation_request->isDone()) + { + if(m_server_creation_request->isSuccess()) + { + new ServerInfoDialog(m_server_creation_request->getCreatedServerID(), true); + } + else + { + sfx_manager->quickSound( "anvil" ); + m_info_widget->setErrorColor(); + m_info_widget->setText(m_server_creation_request->getInfo(), false); + } + delete m_server_creation_request; + m_server_creation_request = NULL; + //m_options_widget->setActivated(); + } + else + { + m_info_widget->setDefaultColor(); + m_info_widget->setText(Online::Messages::creatingServer(), false); + } + } +} // onUpdate + + +// ---------------------------------------------------------------------------- +void CreateServerScreen::serverCreationRequest() +{ + const stringw name = m_name_widget->getText().trim(); + const int max_players = m_max_players_widget->getValue(); + m_info_widget->setErrorColor(); + if (name.size() < 4 || name.size() > 30) + { + m_info_widget->setText(_("Name has to be between 4 and 30 characters long!"), false); + } + else if (max_players < 2 || max_players > 12) + { + m_info_widget->setText(_("The maxinum number of players has to be between 2 and 12."), false); + } + else + { + //m_options_widget->setDeactivated(); + m_server_creation_request = Online::CurrentUser::get()->requestServerCreation(name, max_players); + return; + } + sfx_manager->quickSound("anvil"); +} + +// ---------------------------------------------------------------------------- +void CreateServerScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + if (name == m_options_widget->m_properties[PROP_ID]) + { + const std::string& selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + StateManager::get()->escapePressed(); + } + else if (selection == m_create_widget->m_properties[PROP_ID]) + { + serverCreationRequest(); + } + } +} // eventCallback + +// ---------------------------------------------------------------------------- + +void CreateServerScreen::tearDown() +{ + delete m_server_creation_request; + m_server_creation_request = NULL; +} // tearDown + +// ---------------------------------------------------------------------------- +void CreateServerScreen::onDisabledItemClicked(const std::string& item) +{ + +} // onDisabledItemClicked + +// ---------------------------------------------------------------------------- +void CreateServerScreen::setInitialFocus() +{ +} // setInitialFocus + +// ---------------------------------------------------------------------------- +void CreateServerScreen::onDialogClose() +{ + setInitialFocus(); +} // onDialogClose() diff --git a/src/states_screens/create_server_screen.hpp b/src/states_screens/create_server_screen.hpp new file mode 100644 index 000000000..1a7c23292 --- /dev/null +++ b/src/states_screens/create_server_screen.hpp @@ -0,0 +1,83 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_CREATE_SERVER_SCREEN_HPP +#define HEADER_CREATE_SERVER_SCREEN_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "online/current_user.hpp" + + +namespace GUIEngine { class Widget; class ListWidget; } + +/** + * \brief Handles the main menu + * \ingroup states_screens + */ +class CreateServerScreen : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton +{ +private: + friend class GUIEngine::ScreenSingleton; + + CreateServerScreen(); + + GUIEngine::TextBoxWidget * m_name_widget; + GUIEngine::SpinnerWidget * m_max_players_widget; + + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_create_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + + const Online::CurrentUser::ServerCreationRequest * m_server_creation_request; + + /** \brief Sets which widget has to be focused. Depends on the user state. */ + void setInitialFocus(); + + void serverCreationRequest(); + +public: + + virtual void onUpdate(float delta, irr::video::IVideoDriver* driver) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void tearDown() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void onDisabledItemClicked(const std::string& item) OVERRIDE; + + /** \brief Implements the callback when a dialog gets closed. */ + virtual void onDialogClose() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/credits.cpp b/src/states_screens/credits.cpp index 42b7e399c..c28b3d079 100644 --- a/src/states_screens/credits.cpp +++ b/src/states_screens/credits.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/credits.hpp b/src/states_screens/credits.hpp index 8b54aaf6c..7d4e66c57 100644 --- a/src/states_screens/credits.hpp +++ b/src/states_screens/credits.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/cutscene_gui.cpp b/src/states_screens/cutscene_gui.cpp index 5a74aaa01..05c080126 100644 --- a/src/states_screens/cutscene_gui.cpp +++ b/src/states_screens/cutscene_gui.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Marianne Gagnon +// Copyright (C) 2012-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/cutscene_gui.hpp b/src/states_screens/cutscene_gui.hpp index 7b23a16ec..64deaa0aa 100644 --- a/src/states_screens/cutscene_gui.hpp +++ b/src/states_screens/cutscene_gui.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Marianne Gagnon +// Copyright (C) 2012-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/add_device_dialog.cpp b/src/states_screens/dialogs/add_device_dialog.cpp index 67606f3d4..eb8752dd9 100644 --- a/src/states_screens/dialogs/add_device_dialog.cpp +++ b/src/states_screens/dialogs/add_device_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -44,6 +44,8 @@ using namespace irr::core; AddDeviceDialog::AddDeviceDialog() : ModalDialog(0.90f, 0.80f) { + doInit(); + ScalableFont* font = GUIEngine::getFont(); const int textHeight = GUIEngine::getFontHeight(); const int buttonHeight = textHeight + 10; @@ -178,7 +180,7 @@ GUIEngine::EventPropagation AddDeviceDialog::processEvent { // Remove the previous modal dialog to avoid a warning GUIEngine::ModalDialog::dismiss(); - if(wiimote_manager->askUserToConnectWiimotes() > 0) + if(wiimote_manager->askUserToConnectWiimotes() > 0) ((OptionsScreenInput*)GUIEngine::getCurrentScreen())->rebuildDeviceList(); return GUIEngine::EVENT_BLOCK; diff --git a/src/states_screens/dialogs/add_device_dialog.hpp b/src/states_screens/dialogs/add_device_dialog.hpp index d5b67f81e..033d6cc9f 100644 --- a/src/states_screens/dialogs/add_device_dialog.hpp +++ b/src/states_screens/dialogs/add_device_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/addons_loading.cpp b/src/states_screens/dialogs/addons_loading.cpp index cd52e40d4..e1cefaf61 100644 --- a/src/states_screens/dialogs/addons_loading.cpp +++ b/src/states_screens/dialogs/addons_loading.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2010 Marianne Gagnon, Joerg Henrichs +// Copyright (C) 2009-2013 Marianne Gagnon, Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -31,6 +31,8 @@ #include "io/file_manager.hpp" #include "states_screens/addons_screen.hpp" #include "states_screens/dialogs/message_dialog.hpp" +#include "states_screens/dialogs/vote_dialog.hpp" +#include "states_screens/dialogs/login_dialog.hpp" #include "states_screens/state_manager.hpp" #include "tracks/track_manager.hpp" #include "utils/string_utils.hpp" @@ -43,10 +45,11 @@ using namespace irr::gui; /** Creates a modal dialog with given percentage of screen width and height */ -AddonsLoading::AddonsLoading(const float w, const float h, - const std::string &id) - : ModalDialog(w, h) +AddonsLoading::AddonsLoading(const std::string &id) + : ModalDialog(0.8f, 0.8f) { + m_vote_clicked = false; + m_addon = *(addons_manager->getAddon(id)); m_icon_shown = false; m_download_request = NULL; @@ -197,8 +200,7 @@ void AddonsLoading::escapePressed() // ---------------------------------------------------------------------------- -GUIEngine::EventPropagation - AddonsLoading::processEvent(const std::string& event_source) +GUIEngine::EventPropagation AddonsLoading::processEvent(const std::string& event_source) { GUIEngine::RibbonWidget* actions_ribbon = getWidget("actions"); @@ -236,13 +238,34 @@ GUIEngine::EventPropagation doUninstall(); return GUIEngine::EVENT_BLOCK; } + else if (selection == "vote") + { + voteClicked(); + return GUIEngine::EVENT_BLOCK; + } } return GUIEngine::EVENT_LET; } // processEvent +// ---------------------------------------------------------------------------- +void AddonsLoading::voteClicked() +{ + ModalDialog::dismiss(); + if (Online::CurrentUser::get()->isRegisteredUser()) + new VoteDialog(m_addon.getId()); + else + new LoginDialog(LoginDialog::Registration_Required, new VoteDialog::LoginListener(m_addon.getId())); +} + // ---------------------------------------------------------------------------- void AddonsLoading::onUpdate(float delta) { + if(m_vote_clicked) + { + voteClicked(); + return; + } + if(m_progress->isVisible()) { float progress = m_download_request->getProgress(); diff --git a/src/states_screens/dialogs/addons_loading.hpp b/src/states_screens/dialogs/addons_loading.hpp index 80c336830..93d03ba10 100644 --- a/src/states_screens/dialogs/addons_loading.hpp +++ b/src/states_screens/dialogs/addons_loading.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Lucas Baudin +// Copyright (C) 2010-2013 Lucas Baudin // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -55,16 +55,15 @@ private: * to the progress of a download. */ Request *m_download_request; + bool m_vote_clicked; + public: - AddonsLoading(const float percent_width, - const float percent_height, - const std::string &addon_name); - - ~AddonsLoading(); + AddonsLoading(const std::string &addon_name); + + ~AddonsLoading(); + virtual GUIEngine::EventPropagation processEvent(const std::string& event_source); - virtual void beforeAddingWidgets(); - virtual void init(); /** This function is called by the GUI, all the frame (or somthing like @@ -72,6 +71,7 @@ public: * and do the necessary. * */ void onUpdate(float delta); + void voteClicked(); }; // AddonsLoading diff --git a/src/states_screens/dialogs/change_password_dialog.cpp b/src/states_screens/dialogs/change_password_dialog.cpp new file mode 100644 index 000000000..dd42330c4 --- /dev/null +++ b/src/states_screens/dialogs/change_password_dialog.cpp @@ -0,0 +1,184 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/change_password_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "config/player.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "online/messages.hpp" + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +ChangePasswordDialog::ChangePasswordDialog() : + ModalDialog(0.8f,0.7f) +{ + m_self_destroy = false; + m_success = false; + + loadFromFile("online/change_password.stkgui"); + + m_current_password_widget = getWidget("current_password"); + assert(m_current_password_widget != NULL); + m_current_password_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + m_current_password_widget->setPasswordBox(true,L'*'); + + m_new_password1_widget = getWidget("new_password1"); + assert(m_new_password1_widget != NULL); + m_new_password1_widget->setPasswordBox(true,L'*'); + + m_new_password2_widget = getWidget("new_password2"); + assert(m_new_password2_widget != NULL); + m_new_password2_widget->setPasswordBox(true,L'*'); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_submit_widget = getWidget("submit"); + assert(m_submit_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); +} + +// ----------------------------------------------------------------------------- + +ChangePasswordDialog::~ChangePasswordDialog() +{ +} + +// ----------------------------------------------------------------------------- +void ChangePasswordDialog::submit() +{ + const stringw current_password = m_current_password_widget->getText().trim(); + const stringw new_password1 = m_new_password1_widget->getText().trim(); + const stringw new_password2 = m_new_password2_widget->getText().trim(); + if (current_password.size() < 8 || current_password.size() > 30) + { + sfx_manager->quickSound("anvil"); + m_info_widget->setErrorColor(); + m_info_widget->setText(_("Current password invalid."), false); + } + else if (new_password1.size() < 8 || new_password1.size() > 30) + { + sfx_manager->quickSound("anvil"); + m_info_widget->setErrorColor(); + m_info_widget->setText(_("Password has to be between 8 and 30 characters long!"), false); + } + else if (new_password1 != new_password2) + { + sfx_manager->quickSound("anvil"); + m_info_widget->setErrorColor(); + m_info_widget->setText(_("Passwords don't match!"), false); + } + else + { + m_options_widget->setDeactivated(); + m_info_widget->setDefaultColor(); + Online::CurrentUser::get()->requestPasswordChange(current_password, new_password1, new_password2); + } +} + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation ChangePasswordDialog::processEvent(const std::string& eventSource) +{ + + if (eventSource == m_options_widget->m_properties[PROP_ID]) + { + const std::string& selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_submit_widget->m_properties[PROP_ID]) + { + submit(); + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void ChangePasswordDialog::onEnterPressedInternal() +{ + const int playerID = PLAYER_ID_GAME_MASTER; + if (GUIEngine::isFocusedForPlayer(m_options_widget, playerID)) + return; + if (m_submit_widget->isActivated()) + submit(); +} + +// ----------------------------------------------------------------------------- + +bool ChangePasswordDialog::onEscapePressed() +{ + if (m_cancel_widget->isActivated()) + m_self_destroy = true; + return false; +} + +// ----------------------------------------------------------------------------- +void ChangePasswordDialog::success() +{ + m_info_widget->setDefaultColor(); + m_info_widget->setText(_("Password successfully changed."), false); + m_options_widget->setActivated(); + m_current_password_widget->setText(""); + m_new_password1_widget->setText(""); + m_new_password2_widget->setText(""); +} + +// ----------------------------------------------------------------------------- +void ChangePasswordDialog::error(const irr::core::stringw & error) +{ + sfx_manager->quickSound("anvil"); + m_info_widget->setErrorColor(); + m_info_widget->setText(error, false); + m_options_widget->setActivated(); + m_current_password_widget->setText(""); + m_new_password1_widget->setText(""); + m_new_password2_widget->setText(""); +} + +// ----------------------------------------------------------------------------- + +void ChangePasswordDialog::onUpdate(float dt) +{ + if(!m_options_widget->isActivated()) + m_info_widget->setText(Online::Messages::validatingInfo(), false); + + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + { + ModalDialog::dismiss(); + } +} diff --git a/src/states_screens/dialogs/change_password_dialog.hpp b/src/states_screens/dialogs/change_password_dialog.hpp new file mode 100644 index 000000000..cc699c51d --- /dev/null +++ b/src/states_screens/dialogs/change_password_dialog.hpp @@ -0,0 +1,70 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_CHANGE_PASSWORD_DIALOG_HPP +#define HEADER_CHANGE_PASSWORD_DIALOG_HPP + +#include + +#include "online/current_user.hpp" + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" + +/** + * \brief Dialog that allows a user to sign in + * \ingroup states_screens + */ +class ChangePasswordDialog : public GUIEngine::ModalDialog +{ + +public: + + /** + * Creates a modal dialog with given percentage of screen width and height + */ + ChangePasswordDialog(); + ~ChangePasswordDialog(); + + virtual void onEnterPressedInternal(); + + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual bool onEscapePressed(); + virtual void onUpdate(float dt); + void success(); + void error(const irr::core::stringw & error_message); + +private: + + bool m_self_destroy; + bool m_success; + + GUIEngine::TextBoxWidget * m_current_password_widget; + GUIEngine::TextBoxWidget * m_new_password1_widget; + GUIEngine::TextBoxWidget * m_new_password2_widget; + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_submit_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + + void submit(); +}; + +#endif diff --git a/src/states_screens/dialogs/confirm_resolution_dialog.cpp b/src/states_screens/dialogs/confirm_resolution_dialog.cpp index dfc0e8f51..36f00240f 100644 --- a/src/states_screens/dialogs/confirm_resolution_dialog.cpp +++ b/src/states_screens/dialogs/confirm_resolution_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/confirm_resolution_dialog.hpp b/src/states_screens/dialogs/confirm_resolution_dialog.hpp index 95e9bd4d1..d4a9e01e5 100644 --- a/src/states_screens/dialogs/confirm_resolution_dialog.hpp +++ b/src/states_screens/dialogs/confirm_resolution_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/custom_video_settings.cpp b/src/states_screens/dialogs/custom_video_settings.cpp index 7c6409132..36b66ab01 100644 --- a/src/states_screens/dialogs/custom_video_settings.cpp +++ b/src/states_screens/dialogs/custom_video_settings.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -67,24 +67,39 @@ void CustomVideoSettingsialog::beforeAddingWidgets() else if (UserConfigParams::m_anisotropic == 8) value = 4; else if (UserConfigParams::m_anisotropic == 16) value = 5; else if (UserConfigParams::m_trilinear) value = 1; - filtering->addLabel( L"Bilinear" ); // 0 - filtering->addLabel( L"Trilinear" ); // 1 - filtering->addLabel( L"Anisotropic x2" ); // 2 - filtering->addLabel( L"Anisotropic x4" ); // 3 - filtering->addLabel( L"Anisotropic x8" ); // 4 - filtering->addLabel( L"Anisotropic x16" ); // 5 + filtering->addLabel( _("Bilinear") ); // 0 + filtering->addLabel( _("Trilinear") ); // 1 + filtering->addLabel( _("Anisotropic x2") ); // 2 + filtering->addLabel( _("Anisotropic x4") ); // 3 + filtering->addLabel( _("Anisotropic x8") ); // 4 + filtering->addLabel( _("Anisotropic x16") ); // 5 filtering->setValue( value ); + /* SpinnerWidget* antialias = getWidget("antialiasing"); antialias->addLabel( _("Disabled") ); // 0 antialias->addLabel( L"x2" ); // 1 antialias->addLabel( L"x4" ); // 2 antialias->addLabel( L"x8" ); // 3 antialias->setValue( UserConfigParams::m_antialiasing ); + */ - getWidget("postprocessing")->setState( UserConfigParams::m_postprocess_enabled ); - getWidget("pixelshaders")->setState( UserConfigParams::m_pixel_shaders ); + SpinnerWidget* ssao = getWidget("ssao"); + ssao->addLabel( _("Disabled") ); // 0 + ssao->addLabel( _("low") ); // 1 + ssao->addLabel( _("high") ); // 2 + ssao->setValue( UserConfigParams::m_ssao ); + + SpinnerWidget* shadows = getWidget("shadows"); + shadows->addLabel( _("Disabled") ); // 0 + shadows->addLabel( _("low") ); // 1 + shadows->addLabel( _("high") ); // 2 + shadows->setValue( UserConfigParams::m_shadows ); + + getWidget("motionblur")->setState( UserConfigParams::m_motionblur ); + getWidget("mlaa")->setState( UserConfigParams::m_mlaa ); + getWidget("pixelshaders")->setState(UserConfigParams::m_pixel_shaders); } // ----------------------------------------------------------------------------- @@ -97,14 +112,20 @@ GUIEngine::EventPropagation CustomVideoSettingsialog::processEvent(const std::st getWidget("anim_gfx")->getState(); UserConfigParams::m_weather_effects = getWidget("weather_gfx")->getState(); - UserConfigParams::m_antialiasing = - getWidget("antialiasing")->getValue(); - UserConfigParams::m_postprocess_enabled = - getWidget("postprocessing")->getState(); + //UserConfigParams::m_antialiasing = + // getWidget("antialiasing")->getValue(); + UserConfigParams::m_motionblur = + getWidget("motionblur")->getState(); UserConfigParams::m_show_steering_animations = getWidget("steering_animations")->getValue(); UserConfigParams::m_pixel_shaders = getWidget("pixelshaders")->getState(); + UserConfigParams::m_mlaa = + getWidget("mlaa")->getState(); + UserConfigParams::m_ssao = + getWidget("ssao")->getValue(); + UserConfigParams::m_shadows = + getWidget("shadows")->getValue(); switch (getWidget("filtering")->getValue()) { diff --git a/src/states_screens/dialogs/custom_video_settings.hpp b/src/states_screens/dialogs/custom_video_settings.hpp index 617c1d1b9..75175c39d 100644 --- a/src/states_screens/dialogs/custom_video_settings.hpp +++ b/src/states_screens/dialogs/custom_video_settings.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/enter_player_name_dialog.cpp b/src/states_screens/dialogs/enter_player_name_dialog.cpp index db035d389..1f234d675 100644 --- a/src/states_screens/dialogs/enter_player_name_dialog.cpp +++ b/src/states_screens/dialogs/enter_player_name_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,6 +28,8 @@ #include "guiengine/widgets/text_box_widget.hpp" #include "states_screens/state_manager.hpp" #include "utils/translation.hpp" +#include "utils/string_utils.hpp" + using namespace GUIEngine; using namespace irr; @@ -101,20 +103,7 @@ void EnterPlayerNameDialog::onEnterPressedInternal() // ---- Otherwise, see if we can accept the new name TextBoxWidget* textCtrl = getWidget("textfield"); stringw playerName = textCtrl->getText().trim(); - const int size = playerName.size(); - - // sanity check - int nonEmptyChars = 0; - for (int n=0; n 0 && nonEmptyChars > 0) + if (StringUtils::notEmpty(playerName)) { // check for duplicates const int amount = UserConfigParams::m_all_players.size(); diff --git a/src/states_screens/dialogs/enter_player_name_dialog.hpp b/src/states_screens/dialogs/enter_player_name_dialog.hpp index b7fb0ca27..deb811a0d 100644 --- a/src/states_screens/dialogs/enter_player_name_dialog.hpp +++ b/src/states_screens/dialogs/enter_player_name_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/gp_info_dialog.cpp b/src/states_screens/dialogs/gp_info_dialog.cpp index 395bc5ee5..37c771ab2 100644 --- a/src/states_screens/dialogs/gp_info_dialog.cpp +++ b/src/states_screens/dialogs/gp_info_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,6 +19,7 @@ #include "audio/sfx_manager.hpp" #include "challenges/unlock_manager.hpp" +#include "config/saved_grand_prix.hpp" #include "guiengine/engine.hpp" #include "guiengine/screen.hpp" #include "guiengine/widgets/button_widget.hpp" @@ -139,11 +140,23 @@ GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const floa // ---- Start button ButtonWidget* okBtn = new ButtonWidget(); + ButtonWidget* continueBtn = new ButtonWidget(); + + SavedGrandPrix* saved_gp = SavedGrandPrix::getSavedGP( StateManager::get() + ->getActivePlayerProfile(0) + ->getUniqueID(), + gpIdent, + race_manager->getDifficulty(), + race_manager->getNumberOfKarts(), + race_manager->getNumLocalPlayers()); if (gp_ok) { okBtn->m_properties[PROP_ID] = "start"; okBtn->setText(_("Start Grand Prix")); + + continueBtn->m_properties[PROP_ID] = "continue"; + continueBtn->setText(_("Continue")); } else { @@ -152,7 +165,25 @@ GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const floa okBtn->setBadge(BAD_BADGE); } - okBtn->m_x = m_area.getWidth()/2 - 200; + if (saved_gp && gp_ok) + { + continueBtn->m_x = m_area.getWidth()/2 + 110; + continueBtn->m_y = y2; + continueBtn->m_w = 200; + continueBtn->m_h = m_area.getHeight() - y2 - 15; + continueBtn->setParent(m_irrlicht_window); + m_widgets.push_back(continueBtn); + continueBtn->add(); + continueBtn->getIrrlichtElement()->setTabStop(true); + continueBtn->getIrrlichtElement()->setTabGroup(false); + + okBtn->m_x = m_area.getWidth()/2 - 310; + } + else + { + okBtn->m_x = m_area.getWidth()/2 - 200; + } + okBtn->m_y = y2; okBtn->m_w = 400; okBtn->m_h = m_area.getHeight() - y2 - 15; @@ -163,7 +194,7 @@ GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const floa okBtn->getIrrlichtElement()->setTabGroup(false); okBtn->setFocusForPlayer( PLAYER_ID_GAME_MASTER ); - + } // ------------------------------------------------------------------------------------------------------ @@ -189,7 +220,7 @@ void GPInfoDialog::onEnterPressedInternal() ModalDialog::dismiss(); // Disable accidentally unlocking of a challenge unlock_manager->getCurrentSlot()->setCurrentChallenge(""); - race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false); + race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, false); } // ------------------------------------------------------------------------------------------------------ @@ -201,7 +232,15 @@ GUIEngine::EventPropagation GPInfoDialog::processEvent(const std::string& eventS // Save GP identifier, since dismiss will delete this object. std::string gp_id = m_gp_ident; ModalDialog::dismiss(); - race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false); + race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, false); + return GUIEngine::EVENT_BLOCK; + } + if (eventSource == "continue") + { + // Save GP identifier, since dismiss will delete this object. + std::string gp_id = m_gp_ident; + ModalDialog::dismiss(); + race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, true); return GUIEngine::EVENT_BLOCK; } else if (eventSource == "cannot_start") diff --git a/src/states_screens/dialogs/gp_info_dialog.hpp b/src/states_screens/dialogs/gp_info_dialog.hpp index 206f696d6..b8959bf2d 100644 --- a/src/states_screens/dialogs/gp_info_dialog.hpp +++ b/src/states_screens/dialogs/gp_info_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/login_dialog.cpp b/src/states_screens/dialogs/login_dialog.cpp new file mode 100644 index 000000000..2dd8a4b00 --- /dev/null +++ b/src/states_screens/dialogs/login_dialog.cpp @@ -0,0 +1,217 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/login_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "config/player.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "states_screens/dialogs/registration_dialog.hpp" +#include "states_screens/dialogs/recovery_dialog.hpp" +#include "online/messages.hpp" + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +LoginDialog::LoginDialog(const Message message_type, const LoginDialog::Listener * listener) : + ModalDialog(0.8f,0.9f), m_listener(listener) +{ + m_self_destroy = false; + m_open_registration_dialog = false; + m_open_recovery_dialog = false; + m_success = false; + + loadFromFile("online/login_dialog.stkgui"); + + m_message_widget = getWidget("message"); + assert(m_message_widget != NULL); + + irr::core::stringw message(""); + if (message_type == Normal) + message = _("Fill in your username and password. "); + else if (message_type == Signing_In_Required) + message = _("You need to sign in to be able to use this feature. "); + else if (message_type == Registration_Required) + message = _("You need to be a registered user to enjoy this feature! " + "If you do not have an account yet, you can sign up using the register icon at the bottom."); + else + message = ""; + if (message_type == Normal || message_type == Signing_In_Required) + message += _("If you do not have an account yet, you can choose to sign in as a guest " + "or press the register icon at the bottom to gain access to all the features!"); + m_message_widget->setText(message, false); + + m_username_widget = getWidget("username"); + assert(m_username_widget != NULL); + m_username_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + + m_password_widget = getWidget("password"); + assert(m_password_widget != NULL); + m_password_widget->setPasswordBox(true,L'*'); + + m_remember_widget = getWidget("remember"); + assert(m_remember_widget != NULL); + m_remember_widget->setState(false); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_sign_in_widget = getWidget("sign_in"); + assert(m_sign_in_widget != NULL); + m_recovery_widget = getWidget("recovery"); + assert(m_recovery_widget != NULL); + m_register_widget = getWidget("register"); + assert(m_register_widget != NULL); + m_as_guest_widget = getWidget("as_guest"); + assert(m_as_guest_widget != NULL); + m_as_guest_widget->setDeactivated(); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); +} + +// ----------------------------------------------------------------------------- + +LoginDialog::~LoginDialog() +{ +} + +// ----------------------------------------------------------------------------- +void LoginDialog::login() +{ + const stringw username = m_username_widget->getText().trim(); + const stringw password = m_password_widget->getText().trim(); + if (username.size() < 4 || username.size() > 30 || password.size() < 8 || password.size() > 30) + { + sfx_manager->quickSound("anvil"); + m_info_widget->setErrorColor(); + m_info_widget->setText(_("Username and/or password invalid."), false); + } + else + { + m_options_widget->setDeactivated(); + m_info_widget->setDefaultColor(); + Online::CurrentUser::get()->requestSignIn(username,password, m_remember_widget->getState()); + } +} + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation LoginDialog::processEvent(const std::string& eventSource) +{ + + if (eventSource == m_options_widget->m_properties[PROP_ID]) + { + const std::string& selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_sign_in_widget->m_properties[PROP_ID]) + { + login(); + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_register_widget->m_properties[PROP_ID]) + { + m_open_registration_dialog = true; + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_recovery_widget->m_properties[PROP_ID]) + { + m_open_recovery_dialog = true; + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void LoginDialog::onEnterPressedInternal() +{ + const int playerID = PLAYER_ID_GAME_MASTER; + if (GUIEngine::isFocusedForPlayer(m_options_widget, playerID)) + return; + if (m_sign_in_widget->isActivated()) + login(); +} + +// ----------------------------------------------------------------------------- + +bool LoginDialog::onEscapePressed() +{ + if (m_cancel_widget->isActivated()) + m_self_destroy = true; + return false; +} + +// ----------------------------------------------------------------------------- +void LoginDialog::success() +{ + m_success = true; +} + +// ----------------------------------------------------------------------------- +void LoginDialog::error(const irr::core::stringw & error) +{ + sfx_manager->quickSound("anvil"); + m_info_widget->setErrorColor(); + m_info_widget->setText(error, false); + m_options_widget->setActivated(); + m_as_guest_widget->setDeactivated(); +} + +// ----------------------------------------------------------------------------- + +void LoginDialog::onUpdate(float dt) +{ + if(!m_options_widget->isActivated()) + m_info_widget->setText(Online::Messages::signingIn(), false); + //If we want to open another dialog, we need to close this one first + m_self_destroy |= (m_open_registration_dialog || m_open_recovery_dialog || m_success); + + + bool open_registration_dialog = m_open_registration_dialog; + bool open_recovery_dialog = m_open_recovery_dialog; + bool success = m_success; + const LoginDialog::Listener *listener = m_listener; + + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + { + ModalDialog::dismiss(); + if (open_registration_dialog) + new RegistrationDialog(); + else if (open_recovery_dialog) + new RecoveryDialog(); + else if (success && (listener != NULL) ) + listener->onClose(); + return; + } +} diff --git a/src/states_screens/dialogs/login_dialog.hpp b/src/states_screens/dialogs/login_dialog.hpp new file mode 100644 index 000000000..171708443 --- /dev/null +++ b/src/states_screens/dialogs/login_dialog.hpp @@ -0,0 +1,90 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_LOGIN_DIALOG_HPP +#define HEADER_LOGIN_DIALOG_HPP + +#include + +#include "online/current_user.hpp" + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" + +/** + * \brief Dialog that allows a user to sign in + * \ingroup states_screens + */ +class LoginDialog : public GUIEngine::ModalDialog +{ + +public: + class Listener + { + public : + virtual void onClose() const = 0; + }; + + enum Message + { + Normal = 0, // If the user presses the sign in button himself + Signing_In_Required = 1, // If the user needs to be signed in + Registration_Required = 2 // If the user needs to be registered + }; + + /** + * Creates a modal dialog with given percentage of screen width and height + */ + LoginDialog(const Message = Normal, const LoginDialog::Listener * = NULL); + ~LoginDialog(); + + virtual void onEnterPressedInternal(); + void success(); + void error(const irr::core::stringw & error_message); + + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual bool onEscapePressed(); + virtual void onUpdate(float dt); + +private: + + const Listener * m_listener; + + bool m_self_destroy; + bool m_open_registration_dialog; + bool m_open_recovery_dialog; + bool m_success; + + GUIEngine::LabelWidget * m_message_widget; + GUIEngine::TextBoxWidget * m_username_widget; + GUIEngine::TextBoxWidget * m_password_widget; + GUIEngine::CheckBoxWidget * m_remember_widget; + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_sign_in_widget; + GUIEngine::IconButtonWidget * m_recovery_widget; + GUIEngine::IconButtonWidget * m_register_widget; + GUIEngine::IconButtonWidget * m_as_guest_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + + void login(); +}; + +#endif diff --git a/src/states_screens/dialogs/message_dialog.cpp b/src/states_screens/dialogs/message_dialog.cpp index c1c4f2c36..4d66c4942 100644 --- a/src/states_screens/dialogs/message_dialog.cpp +++ b/src/states_screens/dialogs/message_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -33,19 +33,28 @@ MessageDialog::MessageDialog(const irr::core::stringw &msg, MessageDialogType ty IConfirmDialogListener* listener, bool own_listener) : ModalDialog(0.6f, 0.6f) { - doInit(msg, type, listener, own_listener); + m_msg = msg; + doInit(type, listener, own_listener); } // MessageDialog(stringw, type, listener, own_listener) // ------------------------------------------------------------------------------------------------------ -MessageDialog::MessageDialog(const irr::core::stringw &msg) : +MessageDialog::MessageDialog(const irr::core::stringw &msg, bool from_queue) : ModalDialog(0.6f, 0.6f) { - doInit(msg, MessageDialog::MESSAGE_DIALOG_OK, NULL, false); + m_msg = msg; + if(!from_queue) load(); } // MessageDialog(stringw) // ------------------------------------------------------------------------------------------------------ +void MessageDialog::load() +{ + doInit(MessageDialog::MESSAGE_DIALOG_OK, NULL, false); +} + +// ------------------------------------------------------------------------------------------------------ + MessageDialog::~MessageDialog() { if (m_own_listener) delete m_listener; m_listener = NULL; @@ -58,7 +67,7 @@ MessageDialog::~MessageDialog() // ------------------------------------------------------------------------------------------------------ -void MessageDialog::doInit(const irr::core::stringw &msg, MessageDialogType type, +void MessageDialog::doInit(MessageDialogType type, IConfirmDialogListener* listener, bool own_listener) { if (StateManager::get()->getGameState() == GUIEngine::GAME) @@ -73,7 +82,7 @@ void MessageDialog::doInit(const irr::core::stringw &msg, MessageDialogType type m_own_listener = own_listener; LabelWidget* message = getWidget("title"); - message->setText( msg.c_str(), false ); + message->setText( m_msg.c_str(), false ); // If the dialog is a simple 'OK' dialog, then hide the "Yes" button and // change "Cancel" to "OK" diff --git a/src/states_screens/dialogs/message_dialog.hpp b/src/states_screens/dialogs/message_dialog.hpp index 8d7d58344..6bc952b7d 100644 --- a/src/states_screens/dialogs/message_dialog.hpp +++ b/src/states_screens/dialogs/message_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -69,8 +69,9 @@ private: IConfirmDialogListener* m_listener; bool m_own_listener; - void doInit(const irr::core::stringw &msg, MessageDialogType type, - IConfirmDialogListener* listener, bool own_listener); + irr::core::stringw m_msg; + void doInit(MessageDialogType type, IConfirmDialogListener* listener, + bool own_listener); public: @@ -87,13 +88,13 @@ public: * Variant of MessageDialog where cancelling is not possible (i.e. just shows a message box with OK) * \param msg Message to display in the dialog */ - MessageDialog(const irr::core::stringw &msg); - + MessageDialog(const irr::core::stringw &msg, bool from_queue = false); ~MessageDialog(); virtual void onEnterPressedInternal(); virtual void onUpdate(float dt); + virtual void load(); GUIEngine::EventPropagation processEvent(const std::string& eventSource); }; diff --git a/src/states_screens/dialogs/notification_dialog.cpp b/src/states_screens/dialogs/notification_dialog.cpp new file mode 100644 index 000000000..7e823dfde --- /dev/null +++ b/src/states_screens/dialogs/notification_dialog.cpp @@ -0,0 +1,147 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/notification_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/online_profile_friends.hpp" +#include "utils/translation.hpp" + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +NotificationDialog::NotificationDialog(Type type, const core::stringw info, bool from_queue) + : ModalDialog(0.8f,0.5f) +{ + m_info = info; + m_type = type; + if(!from_queue) load(); +} + +void NotificationDialog::load() +{ + loadFromFile("online/notification_dialog.stkgui"); +} + +void NotificationDialog::beforeAddingWidgets() +{ + m_self_destroy = false; + m_view = false; + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + m_info_widget->setText(m_info, false); + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_view_widget = getWidget("view"); + assert(m_view_widget != NULL); + if(m_type == T_Friends) + { + m_view_widget->setText(_("View Friends")); + } + else if (m_type == T_Achievements) + { + m_view_widget->setText(_("View Achievements")); + } + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); + m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); +} + + + +// ----------------------------------------------------------------------------- +NotificationDialog::~NotificationDialog() +{ +} + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation NotificationDialog::processEvent(const std::string& eventSource) +{ + + if (eventSource == m_options_widget->m_properties[PROP_ID]) + { + const std::string& selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_view_widget->m_properties[PROP_ID]) + { + m_view = true; + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void NotificationDialog::onEnterPressedInternal() +{ + + //If enter was pressed while none of the buttons was focused interpret as close + const int playerID = PLAYER_ID_GAME_MASTER; + if (GUIEngine::isFocusedForPlayer(m_options_widget, playerID)) + return; + m_self_destroy = true; +} + +// ----------------------------------------------------------------------------- + +bool NotificationDialog::onEscapePressed() +{ + if (m_cancel_widget->isActivated()) + m_self_destroy = true; + return false; +} + +// ----------------------------------------------------------------------------- + +void NotificationDialog::onUpdate(float dt) +{ + //If we want to open the registration dialog, we need to close this one first + m_view && (m_self_destroy = true); + + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + { + ModalDialog::dismiss(); + if (m_view) + { + if(m_type == T_Friends) + { + ProfileManager::get()->setVisiting(CurrentUser::get()->getID()); + StateManager::get()->pushScreen(OnlineProfileFriends::getInstance()); + } + else if (m_type == T_Achievements) + { + //FIXME + } + } + return; + } +} diff --git a/src/states_screens/dialogs/notification_dialog.hpp b/src/states_screens/dialogs/notification_dialog.hpp new file mode 100644 index 000000000..70bbcf598 --- /dev/null +++ b/src/states_screens/dialogs/notification_dialog.hpp @@ -0,0 +1,69 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_NOTIFICATION_DIALOG_HPP +#define HEADER_NOTIFICATION_DIALOG_HPP + +#include + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" +#include "online/current_user.hpp" +#include "utils/types.hpp" + + +/** + * \brief Dialog that allows a user to sign in + * \ingroup states_screens + */ +class NotificationDialog : public GUIEngine::ModalDialog +{ +public: + enum Type + { + T_Friends = 1, + T_Achievements = 2 + }; + +private: + bool m_self_destroy; + bool m_view; + Type m_type; + irr::core::stringw m_info; + + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_view_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + +public: + NotificationDialog(Type type, const core::stringw info, bool from_queue = true); + ~NotificationDialog(); + + virtual void beforeAddingWidgets(); + virtual void load(); + + void onEnterPressedInternal(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual bool onEscapePressed(); + virtual void onUpdate(float dt); +}; + +#endif diff --git a/src/states_screens/dialogs/player_info_dialog.cpp b/src/states_screens/dialogs/player_info_dialog.cpp index 6007a7341..c28b3b7e1 100644 --- a/src/states_screens/dialogs/player_info_dialog.cpp +++ b/src/states_screens/dialogs/player_info_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/player_info_dialog.hpp b/src/states_screens/dialogs/player_info_dialog.hpp index 59ff9a361..a4820f236 100644 --- a/src/states_screens/dialogs/player_info_dialog.hpp +++ b/src/states_screens/dialogs/player_info_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/press_a_key_dialog.cpp b/src/states_screens/dialogs/press_a_key_dialog.cpp index 31f266805..5070067c6 100644 --- a/src/states_screens/dialogs/press_a_key_dialog.cpp +++ b/src/states_screens/dialogs/press_a_key_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/press_a_key_dialog.hpp b/src/states_screens/dialogs/press_a_key_dialog.hpp index bb5120607..090cf929a 100644 --- a/src/states_screens/dialogs/press_a_key_dialog.hpp +++ b/src/states_screens/dialogs/press_a_key_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/race_paused_dialog.cpp b/src/states_screens/dialogs/race_paused_dialog.cpp index 3e46dea4b..8da45da27 100644 --- a/src/states_screens/dialogs/race_paused_dialog.cpp +++ b/src/states_screens/dialogs/race_paused_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -146,7 +146,7 @@ GUIEngine::EventPropagation else if (selection == "restart") { ModalDialog::dismiss(); - network_manager->setState(NetworkManager::NS_MAIN_MENU); +// network_manager->setState(NetworkManager::NS_MAIN_MENU); World::getWorld()->scheduleUnpause(); race_manager->rerunRace(); return GUIEngine::EVENT_BLOCK; diff --git a/src/states_screens/dialogs/race_paused_dialog.hpp b/src/states_screens/dialogs/race_paused_dialog.hpp index 74b2e9c62..854e1a1fe 100644 --- a/src/states_screens/dialogs/race_paused_dialog.hpp +++ b/src/states_screens/dialogs/race_paused_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/recovery_dialog.cpp b/src/states_screens/dialogs/recovery_dialog.cpp new file mode 100644 index 000000000..8d4eb9f1e --- /dev/null +++ b/src/states_screens/dialogs/recovery_dialog.cpp @@ -0,0 +1,190 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/recovery_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "config/player.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "online/messages.hpp" + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +RecoveryDialog::RecoveryDialog() : + ModalDialog(0.8f,0.8f) +{ + m_recovery_request = NULL; + m_self_destroy = false; + m_show_recovery_input = true; +} + +// ----------------------------------------------------------------------------- + +RecoveryDialog::~RecoveryDialog() +{ + delete m_recovery_request; +} + +// ----------------------------------------------------------------------------- + +void RecoveryDialog::showRecoveryInput(){ + m_show_recovery_input = false; + clearWindow(); + m_phase = Input; + loadFromFile("online/recovery_input.stkgui"); + + m_username_widget = getWidget("username"); + assert(m_username_widget != NULL); + m_username_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + + m_email_widget = getWidget("email"); + assert(m_email_widget != NULL); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_submit_widget = getWidget("submit"); + assert(m_submit_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); +} + +// ----------------------------------------------------------------------------- + +void RecoveryDialog::showRecoveryInfo(){ + m_show_recovery_info = false; + clearWindow(); + m_phase = Info; + loadFromFile("online/recovery_info.stkgui"); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); +} + +// ----------------------------------------------------------------------------- + +bool RecoveryDialog::onEscapePressed() +{ + return m_cancel_widget->isActivated(); +} + +// ----------------------------------------------------------------------------- + +void RecoveryDialog::processInput() +{ + const stringw username = m_username_widget->getText().trim(); + const stringw email = m_email_widget->getText().trim(); + if (username.size() < 4 || username.size() > 30 || email.size() < 4 || email.size() > 50) + { + sfx_manager->quickSound("anvil"); + m_info_widget->setErrorColor(); + m_info_widget->setText(_("Username and/or email address invalid."), false); + } + else + { + m_info_widget->setDefaultColor(); + m_options_widget->setDeactivated(); + m_recovery_request = CurrentUser::get()->requestRecovery(username, email); + } +} + +// ----------------------------------------------------------------------------- + +GUIEngine::EventPropagation RecoveryDialog::processEvent(const std::string& eventSource) +{ + std::string selection; + if (eventSource == m_options_widget->m_properties[PROP_ID]) + selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + else + selection = eventSource; + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + else if (selection == m_submit_widget->m_properties[PROP_ID]) + { + processInput(); + return GUIEngine::EVENT_BLOCK; + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void RecoveryDialog::onEnterPressedInternal() +{ + + if (GUIEngine::isFocusedForPlayer(m_options_widget, PLAYER_ID_GAME_MASTER)) + return; + if (m_submit_widget->isActivated()) + processInput(); +} + +// ----------------------------------------------------------------------------- + +void RecoveryDialog::onUpdate(float dt) +{ + if(m_recovery_request != NULL) + { + if(m_recovery_request->isDone()) + { + if(m_recovery_request->isSuccess()) + { + m_show_recovery_info = true; + } + else + { + sfx_manager->quickSound( "anvil" ); + m_info_widget->setErrorColor(); + m_info_widget->setText(m_recovery_request->getInfo(), false); + m_options_widget->setActivated(); + } + delete m_recovery_request; + m_recovery_request = NULL; + } + else + { + m_info_widget->setText(Messages::validatingInfo(), false); + } + } + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + ModalDialog::dismiss(); + else if (m_show_recovery_input) + showRecoveryInput(); + else if (m_show_recovery_info) + showRecoveryInfo(); +} diff --git a/src/states_screens/dialogs/recovery_dialog.hpp b/src/states_screens/dialogs/recovery_dialog.hpp new file mode 100644 index 000000000..0f10d34b4 --- /dev/null +++ b/src/states_screens/dialogs/recovery_dialog.hpp @@ -0,0 +1,71 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_RECOVERY_DIALOG_HPP +#define HEADER_RECOVERY_DIALOG_HPP + +#include + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" +#include "online/current_user.hpp" + +/** + * \brief Dialog that allows a user to recover his account + * \ingroup states_screens + */ +class RecoveryDialog : public GUIEngine::ModalDialog +{ +public: + enum Phase + { + Input = 1, + Info = 2, + }; + RecoveryDialog(); + ~RecoveryDialog(); + + void onEnterPressedInternal(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual void onUpdate(float dt); + virtual bool onEscapePressed(); + +private: + Phase m_phase; + bool m_self_destroy; + bool m_show_recovery_input; + bool m_show_recovery_info; + + const Online::XMLRequest * m_recovery_request; + + GUIEngine::TextBoxWidget * m_username_widget; + GUIEngine::TextBoxWidget * m_email_widget; + + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_submit_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + + void showRecoveryInput(); + void showRecoveryInfo(); + void processInput(); +}; + +#endif diff --git a/src/states_screens/dialogs/registration_dialog.cpp b/src/states_screens/dialogs/registration_dialog.cpp new file mode 100644 index 000000000..8fea4ad53 --- /dev/null +++ b/src/states_screens/dialogs/registration_dialog.cpp @@ -0,0 +1,349 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/registration_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "config/player.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "online/messages.hpp" + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +RegistrationDialog::RegistrationDialog() : + ModalDialog(0.8f,0.9f) +{ + m_sign_up_request = NULL; + m_self_destroy = false; + m_show_registration_input = false; + m_show_registration_terms = false; + m_show_registration_info = false; + m_username = ""; + m_email = ""; + m_email_confirm = ""; + m_password = ""; + m_password_confirm = ""; + m_agreement = false; + showRegistrationInput(); +} + +// ----------------------------------------------------------------------------- + +RegistrationDialog::~RegistrationDialog() +{ + delete m_sign_up_request; +} + +// ----------------------------------------------------------------------------- + +void RegistrationDialog::showRegistrationInput() +{ + if(isInited()) + clearWindow(); + m_show_registration_input = false; + m_phase = Input; + loadFromFile("online/registration_input.stkgui"); + + //Password should always be reentered if previous has been clicked, or an error occurred. + m_password = ""; + m_password_confirm = ""; + + m_username_widget = getWidget("username"); + assert(m_username_widget != NULL); + m_username_widget->setText(m_username); + m_username_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + + m_password_widget = getWidget("password"); + assert(m_password_widget != NULL); + m_password_widget->setPasswordBox(true,L'*'); + + m_password_confirm_widget = getWidget("password_confirm"); + assert(m_password_confirm_widget != NULL); + m_password_confirm_widget->setPasswordBox(true,L'*'); + + m_email_widget = getWidget("email"); + assert(m_email_widget != NULL); + m_email_widget->setText(m_email); + + m_email_confirm_widget = getWidget("email_confirm"); + assert(m_email_confirm_widget != NULL); + m_email_confirm_widget->setText(m_email_confirm); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + m_info_widget->setErrorColor(); + m_info_widget->setText(m_registration_error, false); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_next_widget = getWidget("next"); + assert(m_next_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); +} + +// ----------------------------------------------------------------------------- + +void RegistrationDialog::showRegistrationTerms() +{ + m_show_registration_terms = false; + if(isInited()) + clearWindow(); + m_phase = Terms; + loadFromFile("online/registration_terms.stkgui"); + + + ListWidget * terms_widget = getWidget("terms"); + + + terms_widget->addItem("title", "=== STK Terms and Conditions ===", -1 , true ); + terms_widget->addItem("par1", "You must agree to these terms in order to register an account for STK.", -1 , false ); + terms_widget->addItem("par2", + "Still needs actual content. Preferably in an XML document which can then be parsed to be put here." + , -1 , false ); + terms_widget->addItem("par3", + "By checking the box below, you are confirming that you understand these terms." + "If you have any questions or comments regarding these terms," + "one of the members of the development team would gladly assist you." + , -1 , false ); + + + m_accept_terms_widget = getWidget("accepted"); + assert(m_accept_terms_widget != NULL); + m_accept_terms_widget->setState(false); + m_accept_terms_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); //FIXME set focus on the terms + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_previous_widget = getWidget("previous"); + assert(m_previous_widget != NULL); + m_next_widget = getWidget("next"); + assert(m_next_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); + m_next_widget->setDeactivated(); +} + +// ----------------------------------------------------------------------------- + +void RegistrationDialog::showRegistrationInfo() +{ + m_show_registration_info = false; + if(isInited()) + clearWindow(); + m_phase = Info; + loadFromFile("online/registration_info.stkgui"); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); +} + +// ----------------------------------------------------------------------------- + +void RegistrationDialog::processInput() +{ + m_username = m_username_widget->getText().trim(); + m_password = m_password_widget->getText().trim(); + m_password_confirm = m_password_confirm_widget->getText().trim(); + m_email = m_email_widget->getText().trim(); + m_email_confirm = m_email_confirm_widget->getText().trim(); + //FIXME More validation of registration information + m_info_widget->setErrorColor(); + if (m_password != m_password_confirm) + { + m_info_widget->setText(_("Passwords don't match!"), false); + } + else if (m_email != m_email_confirm) + { + m_info_widget->setText(_("Emails don't match!"), false); + } + else if (m_username.size() < 4 || m_username.size() > 30) + { + m_info_widget->setText(_("Username has to be between 4 and 30 characters long!"), false); + } + else if (m_password.size() < 8 || m_password.size() > 30) + { + m_info_widget->setText(_("Password has to be between 8 and 30 characters long!"), false); + } + else if (m_email.size() < 4 || m_email.size() > 50) + { + m_info_widget->setText(_("Email has to be between 4 and 50 characters long!"), false); + } + else + { + m_show_registration_terms = true; + return; + } + sfx_manager->quickSound( "anvil" ); +} + + +// ----------------------------------------------------------------------------- + +bool RegistrationDialog::processInputEvent(const std::string& eventSource) +{ + if (m_phase == Input) + { + if (eventSource == m_next_widget->m_properties[PROP_ID]) + { + processInput(); + return true; + } + } + return false; +} + +// ----------------------------------------------------------------------------- + +bool RegistrationDialog::processTermsEvent(const std::string& eventSource) +{ + if (m_phase == Terms) + { + if (eventSource == m_next_widget->m_properties[PROP_ID]) + { + assert(m_accept_terms_widget->getState()); + m_options_widget->setDeactivated(); + m_info_widget->setDefaultColor(); + m_info_widget->setText(Messages::validatingInfo(), false); + m_sign_up_request = CurrentUser::get()->requestSignUp(m_username, m_password, m_password_confirm, m_email, true); + return true; + } + else if (eventSource == m_accept_terms_widget->m_properties[PROP_ID]) + { + bool new_state = !m_accept_terms_widget->getState(); + m_accept_terms_widget->setState(new_state); + if(new_state) + m_next_widget->setActivated(); + else + m_next_widget->setDeactivated(); + return true; + } + else if (eventSource == m_previous_widget->m_properties[PROP_ID]) + { + m_show_registration_info = true; + return true; + } + } + return false; +} + +// ----------------------------------------------------------------------------- + +bool RegistrationDialog::processInfoEvent(const std::string& eventSource) +{ + return false; +} + +// ----------------------------------------------------------------------------- + +bool RegistrationDialog::onEscapePressed() +{ + return m_cancel_widget->isActivated(); +} + +// ----------------------------------------------------------------------------- + +GUIEngine::EventPropagation RegistrationDialog::processEvent(const std::string& eventSource) +{ + std::string selection; + if (eventSource == m_options_widget->m_properties[PROP_ID]) + selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + else + selection = eventSource; + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + else if (processInputEvent(selection) || processTermsEvent(selection) || processInfoEvent(selection)) + { + return GUIEngine::EVENT_BLOCK; + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void RegistrationDialog::onEnterPressedInternal() +{ + + if (GUIEngine::isFocusedForPlayer(m_options_widget, PLAYER_ID_GAME_MASTER)) + return; + if (m_next_widget->isActivated()) + processInput(); +} + +// ----------------------------------------------------------------------------- + +void RegistrationDialog::onUpdate(float dt) +{ + if (m_phase == Terms) + { + if(m_sign_up_request != NULL) + { + if(m_sign_up_request->isDone()) + { + if(m_sign_up_request->isSuccess()) + { + m_show_registration_info = true; + } + else + { + sfx_manager->quickSound( "anvil" ); + m_show_registration_input = true; + m_registration_error = m_sign_up_request->getInfo(); + } + delete m_sign_up_request; + m_sign_up_request = NULL; + //FIXME m_options_widget->setActivated(); + } + else + { + m_info_widget->setDefaultColor(); + m_info_widget->setText(Messages::validatingInfo(), false); + } + } + } + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + ModalDialog::dismiss(); + else if (m_show_registration_input) + showRegistrationInput(); + else if (m_show_registration_terms) + showRegistrationTerms(); + else if (m_show_registration_info) + showRegistrationInfo(); +} diff --git a/src/states_screens/dialogs/registration_dialog.hpp b/src/states_screens/dialogs/registration_dialog.hpp new file mode 100644 index 000000000..60fc9340d --- /dev/null +++ b/src/states_screens/dialogs/registration_dialog.hpp @@ -0,0 +1,92 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_REGISTRATION_DIALOG_HPP +#define HEADER_REGISTRATION_DIALOG_HPP + +#include + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" +#include "online/current_user.hpp" +/** + * \brief Dialog that allows a user to register + * \ingroup states_screens + */ +class RegistrationDialog : public GUIEngine::ModalDialog +{ +public: + enum Phase + { + Input = 1, + Terms = 2, + Info = 4 + }; + RegistrationDialog(); + ~RegistrationDialog(); + + void onEnterPressedInternal(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual void onUpdate(float dt); + virtual bool onEscapePressed(); + +private: + Phase m_phase; + bool m_self_destroy; + bool m_show_registration_input; + bool m_show_registration_terms; + bool m_show_registration_info; + + const Online::XMLRequest * m_sign_up_request; + + //Saved user input : + irr::core::stringw m_username; + irr::core::stringw m_password; + irr::core::stringw m_password_confirm; + irr::core::stringw m_email; + irr::core::stringw m_email_confirm; + irr::core::stringw m_registration_error; + bool m_agreement; + + GUIEngine::TextBoxWidget * m_username_widget; + GUIEngine::TextBoxWidget * m_password_widget; + GUIEngine::TextBoxWidget * m_password_confirm_widget; + GUIEngine::TextBoxWidget * m_email_widget; + GUIEngine::TextBoxWidget * m_email_confirm_widget; + + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_previous_widget; + GUIEngine::IconButtonWidget * m_next_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + + GUIEngine::CheckBoxWidget * m_accept_terms_widget; + + void showRegistrationInput(); + void showRegistrationTerms(); + void showRegistrationInfo(); + void processInput(); + bool processInputEvent(const std::string& eventSource); + bool processTermsEvent(const std::string& eventSource); + bool processInfoEvent(const std::string& eventSource); + +}; + +#endif diff --git a/src/states_screens/dialogs/select_challenge.cpp b/src/states_screens/dialogs/select_challenge.cpp index 20dec3536..4e396e147 100644 --- a/src/states_screens/dialogs/select_challenge.cpp +++ b/src/states_screens/dialogs/select_challenge.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Marianne Gagnon +// Copyright (C) 2012-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -202,7 +202,7 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin // Initialise global data - necessary even in local games to avoid // many if tests in other places (e.g. if network_game call // network_manager else call race_manager). - network_manager->initCharacterDataStructures(); +// network_manager->initCharacterDataStructures(); // Launch challenge if (eventSource == "novice") @@ -229,7 +229,7 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin } // Sets up kart info, including random list of kart for AI - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(true); irr_driver->hidePointer(); diff --git a/src/states_screens/dialogs/select_challenge.hpp b/src/states_screens/dialogs/select_challenge.hpp index 4ca8bd79e..2a39a17e1 100644 --- a/src/states_screens/dialogs/select_challenge.hpp +++ b/src/states_screens/dialogs/select_challenge.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Marianne Gagnon +// Copyright (C) 2012-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/server_info_dialog.cpp b/src/states_screens/dialogs/server_info_dialog.cpp new file mode 100644 index 000000000..6d8c49123 --- /dev/null +++ b/src/states_screens/dialogs/server_info_dialog.cpp @@ -0,0 +1,175 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/server_info_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "config/player.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "online/current_user.hpp" +#include "online/servers_manager.hpp" +#include "online/messages.hpp" +#include "states_screens/dialogs/registration_dialog.hpp" +#include "states_screens/networking_lobby.hpp" + + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +ServerInfoDialog::ServerInfoDialog(uint32_t server_id, uint32_t host_id, bool from_server_creation) + : ModalDialog(0.8f,0.8f), m_server_id(server_id), m_host_id(host_id) +{ + Log::info("ServerInfoDialog", "Server id is %d, Host id is %d", server_id, host_id); + m_self_destroy = false; + m_enter_lobby = false; + m_from_server_creation = from_server_creation; + m_server_join_request = NULL; + + loadFromFile("online/server_info_dialog.stkgui"); + + m_name_widget = getWidget("name"); + assert(m_name_widget != NULL); + const Server * server = ServersManager::get()->getServerByID(m_server_id); + m_name_widget->setText(server->getName(),false); + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + if (m_from_server_creation) + m_info_widget->setText(_("Server successfully created. You can now join it."), true); + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_join_widget = getWidget("join"); + assert(m_join_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); + m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + +} + +// ----------------------------------------------------------------------------- +ServerInfoDialog::~ServerInfoDialog() +{ + if (m_server_join_request) + delete m_server_join_request; + m_server_join_request = NULL; +} +// ----------------------------------------------------------------------------- +void ServerInfoDialog::requestJoin() +{ + //m_server_join_request = Online::CurrentUser::get()->requestServerJoin(m_server_id); + Online::ServersManager::get()->setJoinedServer(m_server_id); + ProtocolManager::getInstance()->requestStart(new ConnectToServer(m_server_id, m_host_id)); + ModalDialog::dismiss(); + StateManager::get()->pushScreen(NetworkingLobby::getInstance()); + //Online::CurrentUser::release(); +} + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation ServerInfoDialog::processEvent(const std::string& eventSource) +{ + + if (eventSource == m_options_widget->m_properties[PROP_ID]) + { + const std::string& selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_join_widget->m_properties[PROP_ID]) + { + requestJoin(); + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void ServerInfoDialog::onEnterPressedInternal() +{ + + //If enter was pressed while none of the buttons was focused interpret as join event + const int playerID = PLAYER_ID_GAME_MASTER; + if (GUIEngine::isFocusedForPlayer(m_options_widget, playerID)) + return; + requestJoin(); +} + +// ----------------------------------------------------------------------------- + +bool ServerInfoDialog::onEscapePressed() +{ + if (m_cancel_widget->isActivated()) + m_self_destroy = true; + return false; +} + +// ----------------------------------------------------------------------------- + +void ServerInfoDialog::onUpdate(float dt) +{ + if(m_server_join_request != NULL) + { + if(m_server_join_request->isDone()) + { + if(m_server_join_request->isSuccess()) + { + m_enter_lobby = true; + } + else + { + sfx_manager->quickSound( "anvil" ); + m_info_widget->setErrorColor(); + m_info_widget->setText(m_server_join_request->getInfo(), false); + } + delete m_server_join_request; + m_server_join_request = NULL; + } + else + { + m_info_widget->setDefaultColor(); + m_info_widget->setText(Online::Messages::joiningServer(), false); + } + } + + //If we want to open the registration dialog, we need to close this one first + m_enter_lobby && (m_self_destroy = true); + + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + { + ModalDialog::dismiss(); + if (m_from_server_creation) + StateManager::get()->popMenu(); + if (m_enter_lobby) + StateManager::get()->pushScreen(NetworkingLobby::getInstance()); + return; + } +} diff --git a/src/states_screens/dialogs/server_info_dialog.hpp b/src/states_screens/dialogs/server_info_dialog.hpp new file mode 100644 index 000000000..1c040bb78 --- /dev/null +++ b/src/states_screens/dialogs/server_info_dialog.hpp @@ -0,0 +1,70 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_SERVER_INFO_DIALOG_HPP +#define HEADER_SERVER_INFO_DIALOG_HPP + +#include + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "online/server.hpp" +#include "online/current_user.hpp" +#include "utils/types.hpp" + + +/** + * \brief Dialog that allows a user to sign in + * \ingroup states_screens + */ +class ServerInfoDialog : public GUIEngine::ModalDialog +{ + +private: + + bool m_self_destroy; + bool m_enter_lobby; + bool m_from_server_creation; + const Online::CurrentUser::ServerJoinRequest * m_server_join_request; + + const uint32_t m_server_id; + uint32_t m_host_id; + + GUIEngine::LabelWidget * m_name_widget; + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_join_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + + void requestJoin(); + +public: + ServerInfoDialog(uint32_t server_id, uint32_t host_id, bool just_created = false); + ~ServerInfoDialog(); + + void onEnterPressedInternal(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual bool onEscapePressed(); + virtual void onUpdate(float dt); +}; + +#endif diff --git a/src/states_screens/dialogs/track_info_dialog.cpp b/src/states_screens/dialogs/track_info_dialog.cpp index a9ea32304..46199a888 100644 --- a/src/states_screens/dialogs/track_info_dialog.cpp +++ b/src/states_screens/dialogs/track_info_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,7 +28,6 @@ #include "io/file_manager.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" -#include "network/network_manager.hpp" #include "race/highscores.hpp" #include "race/highscore_manager.hpp" #include "race/race_manager.hpp" @@ -121,7 +120,8 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin // Reverse track - const bool reverse_available = track->reverseAvailable(); + const bool reverse_available = track->reverseAvailable() && + race_manager->getMinorMode() != RaceManager::MINOR_MODE_EASTER_EGG; if (reverse_available) { m_checkbox = getWidget("reverse"); diff --git a/src/states_screens/dialogs/track_info_dialog.hpp b/src/states_screens/dialogs/track_info_dialog.hpp index dd7b768db..3367ac5c2 100644 --- a/src/states_screens/dialogs/track_info_dialog.hpp +++ b/src/states_screens/dialogs/track_info_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/tutorial_message_dialog.cpp b/src/states_screens/dialogs/tutorial_message_dialog.cpp index 91cb26c35..3eda7e0cb 100644 --- a/src/states_screens/dialogs/tutorial_message_dialog.cpp +++ b/src/states_screens/dialogs/tutorial_message_dialog.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/tutorial_message_dialog.hpp b/src/states_screens/dialogs/tutorial_message_dialog.hpp index 180767627..cd10d0712 100644 --- a/src/states_screens/dialogs/tutorial_message_dialog.hpp +++ b/src/states_screens/dialogs/tutorial_message_dialog.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/dialogs/user_info_dialog.cpp b/src/states_screens/dialogs/user_info_dialog.cpp new file mode 100644 index 000000000..77cada7dd --- /dev/null +++ b/src/states_screens/dialogs/user_info_dialog.cpp @@ -0,0 +1,229 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/user_info_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/online_profile_overview.hpp" +#include "utils/translation.hpp" +#include "online/messages.hpp" + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +UserInfoDialog::UserInfoDialog(uint32_t showing_id, const core::stringw info, bool error, bool from_queue) + : ModalDialog(0.8f,0.8f), m_showing_id(showing_id) +{ + m_error = error; + m_info = info; + if(!from_queue) load(); +} + +void UserInfoDialog::load() +{ + loadFromFile("online/user_info_dialog.stkgui"); + if(m_error) + m_info_widget->setErrorColor(); + m_name_widget->setText(m_profile->getUserName(),false); + m_info_widget->setText(m_info, false); + if(m_remove_widget->isVisible() && !m_profile->isFriend()) + m_remove_widget->setLabel("Cancel Request"); +} + +void UserInfoDialog::beforeAddingWidgets() +{ + m_profile = ProfileManager::get()->getProfileByID(m_showing_id); + m_self_destroy = false; + m_enter_profile = false; + m_processing = false; + m_name_widget = getWidget("name"); + assert(m_name_widget != NULL); + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_remove_widget = getWidget("remove"); + assert(m_remove_widget != NULL); + m_friend_widget = getWidget("friend"); + assert(m_friend_widget != NULL); + m_accept_widget = getWidget("accept"); + assert(m_accept_widget != NULL); + m_decline_widget = getWidget("decline"); + assert(m_decline_widget != NULL); + m_enter_widget = getWidget("enter"); + assert(m_enter_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); + m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + + m_accept_widget->setVisible(false); + m_decline_widget->setVisible(false); + m_remove_widget->setVisible(false); + if(m_profile->isCurrentUser()) + { + m_friend_widget->setVisible(false); + } + if(m_profile->isFriend()) + { + m_friend_widget->setVisible(false); + m_remove_widget->setVisible(true); + } + + Profile::RelationInfo * relation_info = m_profile->getRelationInfo(); + if(relation_info != NULL) + { + if(relation_info->isPending()) + { + m_friend_widget->setVisible(false); + if(relation_info->isAsker()) + { + m_accept_widget->setVisible(true); + m_decline_widget->setVisible(true); + } + else + { + m_remove_widget->setVisible(true); + } + } + } + +} + + + +// ----------------------------------------------------------------------------- +UserInfoDialog::~UserInfoDialog() +{ +} + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation UserInfoDialog::processEvent(const std::string& eventSource) +{ + + if (eventSource == m_options_widget->m_properties[PROP_ID]) + { + const std::string& selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_enter_widget->m_properties[PROP_ID]) + { + ProfileManager::get()->setVisiting(m_profile->getID()); + m_enter_profile = true; + m_options_widget->setDeactivated(); + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_friend_widget->m_properties[PROP_ID]) + { + CurrentUser::get()->requestFriendRequest(m_profile->getID()); + m_processing = true; + m_options_widget->setDeactivated(); + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_remove_widget->m_properties[PROP_ID]) + { + if(m_profile->getRelationInfo()->isPending()) + CurrentUser::get()->requestCancelFriend(m_profile->getID()); + else + CurrentUser::get()->requestRemoveFriend(m_profile->getID()); + m_processing = true; + m_options_widget->setDeactivated(); + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_accept_widget->m_properties[PROP_ID]) + { + CurrentUser::get()->requestAcceptFriend(m_profile->getID()); + m_processing = true; + m_options_widget->setDeactivated(); + return GUIEngine::EVENT_BLOCK; + } + else if(selection == m_decline_widget->m_properties[PROP_ID]) + { + CurrentUser::get()->requestDeclineFriend(m_profile->getID()); + m_processing = true; + m_options_widget->setDeactivated(); + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- +void UserInfoDialog::deactivate() +{ + m_options_widget->setDeactivated(); +} + +// ----------------------------------------------------------------------------- +void UserInfoDialog::activate() +{ + +} + + +// ----------------------------------------------------------------------------- + +void UserInfoDialog::onEnterPressedInternal() +{ + + //If enter was pressed while none of the buttons was focused interpret as join event + const int playerID = PLAYER_ID_GAME_MASTER; + if (GUIEngine::isFocusedForPlayer(m_options_widget, playerID)) + return; + m_self_destroy = true; +} + +// ----------------------------------------------------------------------------- + +bool UserInfoDialog::onEscapePressed() +{ + if (m_cancel_widget->isActivated()) + m_self_destroy = true; + return false; +} + +// ----------------------------------------------------------------------------- + + +void UserInfoDialog::onUpdate(float dt) +{ + if(m_processing) m_info_widget->setText(Messages::processing(), false); + + //If we want to open the registration dialog, we need to close this one first + m_enter_profile && (m_self_destroy = true); + + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + { + ModalDialog::dismiss(); + if (m_enter_profile) + StateManager::get()->replaceTopMostScreen(OnlineProfileOverview::getInstance()); + return; + } +} diff --git a/src/states_screens/dialogs/user_info_dialog.hpp b/src/states_screens/dialogs/user_info_dialog.hpp new file mode 100644 index 000000000..c9f16eec3 --- /dev/null +++ b/src/states_screens/dialogs/user_info_dialog.hpp @@ -0,0 +1,79 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_USER_INFO_DIALOG_HPP +#define HEADER_USER_INFO_DIALOG_HPP + +#include + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" +#include "online/profile_manager.hpp" +#include "online/current_user.hpp" +#include "utils/types.hpp" + + +/** + * \brief Dialog that allows a user to sign in + * \ingroup states_screens + */ +class UserInfoDialog : public GUIEngine::ModalDialog +{ + +private: + + bool m_self_destroy; + bool m_enter_profile; + bool m_processing; + + bool m_error; + irr::core::stringw m_info; + + const uint32_t m_showing_id; + Online::Profile * m_profile; + + GUIEngine::LabelWidget * m_name_widget; + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_remove_widget; + GUIEngine::IconButtonWidget * m_friend_widget; + GUIEngine::IconButtonWidget * m_accept_widget; + GUIEngine::IconButtonWidget * m_decline_widget; + GUIEngine::IconButtonWidget * m_enter_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + + void requestJoin(); + void activate(); + void deactivate(); + +public: + UserInfoDialog(uint32_t showing_id, const core::stringw info = "", bool error = false, bool from_queue = false); + ~UserInfoDialog(); + + virtual void beforeAddingWidgets(); + virtual void load(); + + void onEnterPressedInternal(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual bool onEscapePressed(); + virtual void onUpdate(float dt); +}; + +#endif diff --git a/src/states_screens/dialogs/vote_dialog.cpp b/src/states_screens/dialogs/vote_dialog.cpp new file mode 100644 index 000000000..eab14fc20 --- /dev/null +++ b/src/states_screens/dialogs/vote_dialog.cpp @@ -0,0 +1,176 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/dialogs/vote_dialog.hpp" + +#include + +#include "audio/sfx_manager.hpp" +#include "guiengine/engine.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "online/current_user.hpp" +#include "online/messages.hpp" + + + +using namespace GUIEngine; +using namespace irr; +using namespace irr::gui; +using namespace Online; + +// ----------------------------------------------------------------------------- + +VoteDialog::VoteDialog(const std::string & addon_id) + : ModalDialog(0.8f,0.6f), m_addon_id(addon_id) +{ + m_fetch_vote_request = NULL; + m_perform_vote_request = NULL; + m_self_destroy = false; + loadFromFile("online/vote_dialog.stkgui"); + + m_info_widget = getWidget("info"); + assert(m_info_widget != NULL); + + m_rating_widget = getWidget("rating"); + assert(m_rating_widget != NULL); + m_rating_widget->setRating(0); + m_rating_widget->allowVoting(); + m_options_widget = getWidget("options"); + assert(m_options_widget != NULL); + m_cancel_widget = getWidget("cancel"); + assert(m_cancel_widget != NULL); + m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + + m_fetch_vote_request = CurrentUser::get()->requestGetAddonVote(m_addon_id); + + m_rating_widget->setDeactivated(); + m_cancel_widget->setDeactivated(); +} + +// ----------------------------------------------------------------------------- +VoteDialog::~VoteDialog() +{ + delete m_fetch_vote_request; + delete m_perform_vote_request; +} + +// ----------------------------------------------------------------------------- + +bool VoteDialog::onEscapePressed() +{ + if (m_cancel_widget->isActivated()) + m_self_destroy = true; + return false; +} + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation VoteDialog::processEvent(const std::string& eventSource) +{ + + if (eventSource == m_rating_widget->m_properties[PROP_ID]) + { + m_perform_vote_request = CurrentUser::get()->requestSetAddonVote(m_addon_id, m_rating_widget->getRating()); + m_rating_widget->setDeactivated(); + m_cancel_widget->setDeactivated(); + return GUIEngine::EVENT_BLOCK; + } + + if (eventSource == m_options_widget->m_properties[PROP_ID]) + { + const std::string& selection = m_options_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (selection == m_cancel_widget->m_properties[PROP_ID]) + { + m_self_destroy = true; + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- + +void VoteDialog::onUpdate(float dt) +{ + if(m_fetch_vote_request != NULL) + { + if(m_fetch_vote_request->isDone()) + { + if(m_fetch_vote_request->isSuccess()) + { + m_info_widget->setDefaultColor(); + std::string voted(""); + m_fetch_vote_request->getResult()->get("voted", &voted); + if(voted == "yes") + { + float rating; + m_fetch_vote_request->getResult()->get("rating", &rating); + m_rating_widget->setRating(rating); + m_info_widget->setText(_("You can adapt your previous rating by clicking the stars beneath."), false); + } + else if(voted == "no") + { + m_info_widget->setText(_("You have not yet voted for this addon. Select your desired rating by clicking the stars beneath"), false); + } + m_cancel_widget->setActivated(); + m_rating_widget->setActivated(); + } + else + { + sfx_manager->quickSound( "anvil" ); + m_info_widget->setErrorColor(); + m_info_widget->setText(m_fetch_vote_request->getInfo(), false); + m_cancel_widget->setActivated(); + } + delete m_fetch_vote_request; + m_fetch_vote_request = NULL; + } + else + { + m_info_widget->setText(irr::core::stringw(_("Fetching last vote")) + Messages::loadingDots(), false); + } + } + if(m_perform_vote_request != NULL) + { + if(m_perform_vote_request->isDone()) + { + if(m_perform_vote_request->isSuccess()) + { + m_info_widget->setDefaultColor(); + m_info_widget->setText(_("Vote successful! You can now close the window."), false); + m_cancel_widget->setActivated(); + } + else + { + sfx_manager->quickSound( "anvil" ); + m_info_widget->setErrorColor(); + m_info_widget->setText(m_perform_vote_request->getInfo(), false); + m_cancel_widget->setActivated(); + m_rating_widget->setActivated(); + } + delete m_perform_vote_request; + m_perform_vote_request = NULL; + } + else + { + m_info_widget->setText(irr::core::stringw(_("Performing vote")) + Messages::loadingDots(), false); + } + } + if (m_self_destroy) + ModalDialog::dismiss(); +} diff --git a/src/states_screens/dialogs/vote_dialog.hpp b/src/states_screens/dialogs/vote_dialog.hpp new file mode 100644 index 000000000..07fa30746 --- /dev/null +++ b/src/states_screens/dialogs/vote_dialog.hpp @@ -0,0 +1,66 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_VOTE_DIALOG_HPP +#define HEADER_VOTE_DIALOG_HPP + +#include + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" +#include "online/current_user.hpp" +#include "states_screens/dialogs/login_dialog.hpp" + + +/** + * \brief Dialog that allows a user to sign in + * \ingroup states_screens + */ +class VoteDialog : public GUIEngine::ModalDialog +{ +public : + class LoginListener : public LoginDialog::Listener + { + const std::string m_addon_id; + public : + LoginListener(const std::string & addon_id) : m_addon_id(addon_id) {} + virtual void onClose() const { new VoteDialog(m_addon_id); } + }; + +private: + const std::string m_addon_id; + bool m_self_destroy; + const Online::XMLRequest * m_fetch_vote_request; + const Online::CurrentUser::SetAddonVoteRequest * m_perform_vote_request; + + GUIEngine::LabelWidget * m_info_widget; + + GUIEngine::RatingBarWidget * m_rating_widget; + + GUIEngine::RibbonWidget * m_options_widget; + GUIEngine::IconButtonWidget * m_cancel_widget; + +public: + VoteDialog(const std::string & addon_id); + ~VoteDialog(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + virtual void onUpdate(float dt); + virtual bool onEscapePressed(); +}; + +#endif diff --git a/src/states_screens/easter_egg_screen.cpp b/src/states_screens/easter_egg_screen.cpp new file mode 100644 index 000000000..b107fe5bf --- /dev/null +++ b/src/states_screens/easter_egg_screen.cpp @@ -0,0 +1,301 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009-2013 Marianne Gagnon +// +// 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 "challenges/unlock_manager.hpp" +#include "graphics/irr_driver.hpp" +#include "guiengine/widget.hpp" +#include "guiengine/widgets/dynamic_ribbon_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "io/file_manager.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/easter_egg_screen.hpp" +#include "states_screens/dialogs/track_info_dialog.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" +#include "utils/translation.hpp" + +#include + +using namespace GUIEngine; +using namespace irr::core; +using namespace irr::video; + +static const char ALL_TRACK_GROUPS_ID[] = "all"; + +DEFINE_SCREEN_SINGLETON( EasterEggScreen ); + +// ----------------------------------------------------------------------------- + +EasterEggScreen::EasterEggScreen() : Screen("easter_egg.stkgui") +{ +} + +// ----------------------------------------------------------------------------- + +void EasterEggScreen::loadedFromFile() +{ +} + +// ----------------------------------------------------------------------------- + +void EasterEggScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + // -- track selection screen + if (name == "tracks") + { + DynamicRibbonWidget* w2 = dynamic_cast(widget); + if (w2 != NULL) + { + const std::string selection = w2->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if(UserConfigParams::logGUI()) + std::cout << "Clicked on track " << selection.c_str() + << std::endl; + + UserConfigParams::m_last_track = selection; + + if (selection == "random_track") + { + RibbonWidget* tabs = this->getWidget("trackgroups"); + assert( tabs != NULL ); + + if (m_random_track_list.empty()) return; + + std::string track = m_random_track_list.front(); + m_random_track_list.pop_front(); + m_random_track_list.push_back(track); + Track* clickedTrack = track_manager->getTrack( track ); + + + if (clickedTrack != NULL) + { + ITexture* screenshot = + irr_driver->getTexture( clickedTrack->getScreenshotFile(), + "While loading screenshot for track '%s':", + clickedTrack->getFilename() ); + + new TrackInfoDialog(selection, clickedTrack->getIdent(), + translations->fribidize(clickedTrack->getName()), + screenshot, 0.8f, 0.7f); + } + + } + else if (selection == "locked") + { + unlock_manager->playLockSound(); + } + else if (selection == RibbonWidget::NO_ITEM_ID) + { + } + else + { + Track* clickedTrack = track_manager->getTrack(selection); + if (clickedTrack != NULL) + { + ITexture* screenshot = + irr_driver->getTexture( clickedTrack->getScreenshotFile(), + "While loading screenshot for track '%s'", + clickedTrack->getFilename()); + + new TrackInfoDialog(selection, clickedTrack->getIdent(), + translations->fribidize(clickedTrack->getName()), + screenshot, 0.8f, 0.7f); + } + } + } + } + else if (name == "trackgroups") + { + RibbonWidget* tabs = this->getWidget("trackgroups"); + assert( tabs != NULL ); + UserConfigParams::m_last_used_track_group = tabs->getSelectionIDString(0); + buildTrackList(); + } + else if (name == "back") + { + StateManager::get()->escapePressed(); + } +} + +// ----------------------------------------------------------------------------- + +void EasterEggScreen::beforeAddingWidget() +{ + Screen::init(); + // Dynamically add tabs + RibbonWidget* tabs = this->getWidget("trackgroups"); + assert( tabs != NULL ); + + tabs->clearAllChildren(); + + const std::vector& groups = track_manager->getAllTrackGroups(); + const int group_amount = groups.size(); + + if (group_amount > 1) + { + //I18N: name of the tab that will show tracks from all groups + tabs->addTextChild( _("All"), ALL_TRACK_GROUPS_ID ); + } + + // Make group names being picked up by gettext +#define FOR_GETTEXT_ONLY(x) + //I18N: track group name + FOR_GETTEXT_ONLY( _("standard") ) + //I18N: track group name + FOR_GETTEXT_ONLY( _("Add-Ons") ) + + // add others after + for (int n=0; naddTextChild( _(groups[n].c_str()), groups[n] ); + } + + int num_of_arenas=0; + for (unsigned int n=0; ngetNumberOfTracks(); n++) //iterate through tracks to find how many are arenas + { + Track* temp = track_manager->getTrack(n); + if(temp->hasEasterEggs()) + num_of_arenas++; + } + + DynamicRibbonWidget* tracks_widget = this->getWidget("tracks"); + assert( tracks_widget != NULL ); + tracks_widget->setItemCountHint(num_of_arenas); //set the item hint to that number to prevent weird formatting +} + +// ----------------------------------------------------------------------------- + +void EasterEggScreen::init() +{ + DynamicRibbonWidget* tracks_widget = this->getWidget("tracks"); + assert( tracks_widget != NULL ); + + RibbonWidget* tabs = this->getWidget("trackgroups"); + assert( tabs != NULL ); + tabs->select(UserConfigParams::m_last_used_track_group, PLAYER_ID_GAME_MASTER); + + + buildTrackList(); + + // select old track for the game master (if found) + irr_driver->setTextureErrorMessage( + "While loading screenshot in track screen for last track '%s':", + UserConfigParams::m_last_track); + if (!tracks_widget->setSelection(UserConfigParams::m_last_track, + PLAYER_ID_GAME_MASTER, true)) + { + tracks_widget->setSelection(0, PLAYER_ID_GAME_MASTER, true); + } + irr_driver->unsetTextureErrorMessage(); +} + +// ----------------------------------------------------------------------------- + +void EasterEggScreen::buildTrackList() +{ + DynamicRibbonWidget* tracks_widget = this->getWidget("tracks"); + assert( tracks_widget != NULL ); + + RibbonWidget* tabs = this->getWidget("trackgroups"); + assert( tabs != NULL ); + + // Reset track list everytime (accounts for locking changes, etc.) + tracks_widget->clearItems(); + m_random_track_list.clear(); + + const std::string curr_group_name = tabs->getSelectionIDString(0); + + // Build track list + if (curr_group_name == ALL_TRACK_GROUPS_ID) + { + const int trackAmount = track_manager->getNumberOfTracks(); + + for (int n=0; ngetTrack( n ); + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG + && !curr->hasEasterEggs()) + continue; + if (curr->isArena() || curr->isSoccer()) continue; + if (curr->isInternal()) continue; + + if (unlock_manager->getCurrentSlot()->isLocked(curr->getIdent())) + { + tracks_widget->addItem( _("Locked : solve active challenges to gain access to more!"), + "locked", curr->getScreenshotFile(), LOCKED_BADGE, + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + } + else + { + tracks_widget->addItem(translations->fribidize(curr->getName()), curr->getIdent(), + curr->getScreenshotFile(), 0, + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE ); + m_random_track_list.push_back(curr->getIdent()); + } + } + + } + else + { + const std::vector& curr_group = track_manager->getTracksInGroup( curr_group_name ); + const int trackAmount = curr_group.size(); + + 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->isSoccer()) continue; + if (curr->isInternal()) continue; + + if (unlock_manager->getCurrentSlot()->isLocked(curr->getIdent())) + { + tracks_widget->addItem( _("Locked : solve active challenges to gain access to more!"), + "locked", curr->getScreenshotFile(), LOCKED_BADGE, + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + } + else + { + tracks_widget->addItem(translations->fribidize(curr->getName()), curr->getIdent(), + curr->getScreenshotFile(), 0 /* no badge */, + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE ); + m_random_track_list.push_back(curr->getIdent()); + } + } + } + + tracks_widget->addItem(_("Random Track"), "random_track", "/gui/track_random.png", + 0 /* no badge */, IconButtonWidget::ICON_PATH_TYPE_RELATIVE); + + tracks_widget->updateItemDisplay(); + std::random_shuffle( m_random_track_list.begin(), m_random_track_list.end() ); +} + +// ----------------------------------------------------------------------------- + +void EasterEggScreen::setFocusOnTrack(const std::string& trackName) +{ + DynamicRibbonWidget* tracks_widget = this->getWidget("tracks"); + assert( tracks_widget != NULL ); + + // only the game master can select tracks, so it's safe to use 'PLAYER_ID_GAME_MASTER' + tracks_widget->setSelection(trackName, PLAYER_ID_GAME_MASTER, true); +} + +// ----------------------------------------------------------------------------- diff --git a/src/states_screens/easter_egg_screen.hpp b/src/states_screens/easter_egg_screen.hpp new file mode 100644 index 000000000..605fa526c --- /dev/null +++ b/src/states_screens/easter_egg_screen.hpp @@ -0,0 +1,61 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009-2013 Marianne Gagnon +// +// 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 HEADER_EASTER_EGG_SCREEN_HPP +#define HEADER_EASTER_EGG_SCREEN_HPP + +#include "guiengine/screen.hpp" +#include + +namespace GUIEngine { class Widget; } + +/** + * \brief screen where the user can select a track + * \ingroup states_screens + */ +class EasterEggScreen : public GUIEngine::Screen, public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; + + EasterEggScreen(); + + /** adds the tracks from the current track group into the tracks ribbon */ + void buildTrackList(); + + std::deque m_random_track_list; + +public: + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + + void setFocusOnTrack(const std::string& trackName); + +}; + +#endif diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp index 9cfcf611a..dcb56b6ce 100644 --- a/src/states_screens/feature_unlocked.cpp +++ b/src/states_screens/feature_unlocked.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -300,15 +300,16 @@ void FeatureUnlockedCutScene::init() 120, 120)); const core::vector3df &sun_pos = core::vector3df( 0, 200, 100.0f ); - m_light = - irr_driver->getSceneManager()->addLightSceneNode(NULL, sun_pos, - video::SColorf(1.0f,1.0f,1.0f), - 10000.0f /* radius */); + m_light = irr_driver->addLight(sun_pos, 10000.0f, 1, 1, 1); #ifdef DEBUG m_light->setName("light"); #endif - m_light->getLightData().DiffuseColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); - m_light->getLightData().SpecularColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + + if (!irr_driver->isGLSL()) + { + ((scene::ILightSceneNode *) m_light)->getLightData().DiffuseColor = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + ((scene::ILightSceneNode *) m_light)->getLightData().SpecularColor = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + } const int unlockedStuffCount = m_unlocked_stuff.size(); @@ -330,7 +331,7 @@ void FeatureUnlockedCutScene::init() m_unlocked_stuff[n].m_root_gift_node = kart_model->attachModel(true); kart_model->setAnimation(KartModel::AF_DEFAULT); float susp[4]={0,0,0,0}; - kart_model->update(0.0f, 0.0f, susp, 0.0f); + kart_model->update(0.0f, 0.0f, 0.0f, susp, 0.0f); #ifdef DEBUG m_unlocked_stuff[n].m_root_gift_node->setName("unlocked kart"); diff --git a/src/states_screens/feature_unlocked.hpp b/src/states_screens/feature_unlocked.hpp index 761dca4e6..867825442 100644 --- a/src/states_screens/feature_unlocked.hpp +++ b/src/states_screens/feature_unlocked.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -113,7 +113,7 @@ class FeatureUnlockedCutScene : public GUIEngine::Screen, public GUIEngine::Scre irr::scene::IAnimatedMeshSceneNode *m_chest; /** The scene node for the light. */ - irr::scene::ILightSceneNode* m_light; + irr::scene::ISceneNode* m_light; //#define USE_IRRLICHT_BUG_WORKAROUND diff --git a/src/states_screens/grand_prix_lose.cpp b/src/states_screens/grand_prix_lose.cpp index d95b8c0cf..449da86d6 100644 --- a/src/states_screens/grand_prix_lose.cpp +++ b/src/states_screens/grand_prix_lose.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -155,11 +155,14 @@ void GrandPrixLose::init() sceneManager->setAmbientLight(video::SColor(255, 120, 120, 120)); const core::vector3df &sun_pos = core::vector3df( 0, 200, 100.0f ); - m_light = irr_driver->getSceneManager()->addLightSceneNode(NULL, sun_pos, - video::SColorf(1.0f,1.0f,1.0f), - 300.0f /* radius */); - m_light->getLightData().DiffuseColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); - m_light->getLightData().SpecularColor = irr::video::SColorf(1.0f, 0.0f, 0.0f, 0.0f); + m_light = irr_driver->addLight(sun_pos, 300.0f, 1, 1, 1); + + if (!irr_driver->isGLSL()) + { + scene::ILightSceneNode *lnode = (scene::ILightSceneNode *) m_light; + lnode->getLightData().DiffuseColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + lnode->getLightData().SpecularColor = irr::video::SColorf(1.0f, 0.0f, 0.0f, 0.0f); + } } // init // ------------------------------------------------------------------------------------- @@ -340,7 +343,7 @@ void GrandPrixLose::setKarts(std::vector ident_arg) kart_main_node->updateAbsolutePosition(); kart_main_node->setRotation(vector3df(0, 90, 0)); float susp[4]={0,0,0,0}; - kart_model->update(0.0f, 0.0f, susp, 0.0f); + kart_model->update(0.0f, 0.0f, 0.0f, susp, 0.0f); } else { diff --git a/src/states_screens/grand_prix_lose.hpp b/src/states_screens/grand_prix_lose.hpp index ba286aa4d..7395e5ea0 100644 --- a/src/states_screens/grand_prix_lose.hpp +++ b/src/states_screens/grand_prix_lose.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -53,7 +53,7 @@ class GrandPrixLose : public GUIEngine::Screen, public GUIEngine::ScreenSingleto irr::scene::ISceneNode* m_sky; irr::scene::ICameraSceneNode* m_camera; - irr::scene::ILightSceneNode* m_light; + irr::scene::ISceneNode* m_light; /** A copy of the kart model for each kart used. */ std::vector m_all_kart_models; diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index 16e31c329..1aed4ba40 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -210,12 +210,16 @@ void GrandPrixWin::init() sceneManager->setAmbientLight(video::SColor(255, 95, 95, 95)); const core::vector3df &sun_pos = core::vector3df( 0, 200, 100.0f ); - m_light = irr_driver->getSceneManager()->addLightSceneNode(NULL, sun_pos, - video::SColorf(0.25f,0.25f,0.25f), - 300.0f /* radius */); - m_light->getLightData().DiffuseColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); - m_light->getLightData().AmbientColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); - m_light->getLightData().SpecularColor = irr::video::SColorf(0.0f, 0.0f, 0.0f, 1.0f); + m_light = irr_driver->addLight(sun_pos, 300.0f, 0.25f, 0.25f, 0.25f); + + m_finish_sound = sfx_manager->quickSound("gp_end"); + if (!irr_driver->isGLSL()) + { + scene::ILightSceneNode *lnode = (scene::ILightSceneNode *) m_light; + lnode->getLightData().DiffuseColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); + lnode->getLightData().AmbientColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); + lnode->getLightData().SpecularColor = irr::video::SColorf(0.0f, 0.0f, 0.0f, 1.0f); + } sfx_manager->quickSound("gp_end"); } // init @@ -254,6 +258,12 @@ void GrandPrixWin::tearDown() delete m_unlocked_label; m_unlocked_label = NULL; } + + if (m_finish_sound != NULL && + m_finish_sound->getStatus() == SFXManager::SFX_PLAYING) + { + m_finish_sound->stop(); + } } // tearDown // ------------------------------------------------------------------------------------- @@ -451,7 +461,7 @@ void GrandPrixWin::setKarts(const std::string idents_arg[3]) m_kart_z[n]) ); kart_main_node->setScale( core::vector3df(0.4f, 0.4f, 0.4f) ); float susp[4]={0,0,0,0}; - kart_model->update(0.0f, 0.0f, susp, 0.0f); + kart_model->update(0.0f, 0.0f, 0.0f, susp, 0.0f); } else { diff --git a/src/states_screens/grand_prix_win.hpp b/src/states_screens/grand_prix_win.hpp index 85fa5afae..8a5ca7e0c 100644 --- a/src/states_screens/grand_prix_win.hpp +++ b/src/states_screens/grand_prix_win.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 SuperTuxKart-Team +// Copyright (C) 2010-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,6 +19,7 @@ #ifndef HEADER_GRAND_PRIX_WIN_HPP #define HEADER_GRAND_PRIX_WIN_HPP +#include "audio/sfx_base.hpp" #include "guiengine/screen.hpp" #include "karts/kart_model.hpp" @@ -53,7 +54,7 @@ class GrandPrixWin : public GUIEngine::Screen, public GUIEngine::ScreenSingleton irr::scene::ISceneNode* m_sky; irr::scene::ICameraSceneNode* m_camera; - irr::scene::ILightSceneNode* m_light; + irr::scene::ISceneNode* m_light; GUIEngine::LabelWidget* m_unlocked_label; @@ -67,6 +68,7 @@ class GrandPrixWin : public GUIEngine::Screen, public GUIEngine::ScreenSingleton float m_camera_target_x, m_camera_target_z; MusicInformation* m_music; + SFXBase* m_finish_sound; public: diff --git a/src/states_screens/help_screen_1.cpp b/src/states_screens/help_screen_1.cpp index eac26e2ec..a75d43286 100644 --- a/src/states_screens/help_screen_1.cpp +++ b/src/states_screens/help_screen_1.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,7 +24,6 @@ #include "input/device_manager.hpp" #include "input/input_manager.hpp" #include "karts/kart_properties_manager.hpp" -#include "network/network_manager.hpp" #include "race/race_manager.hpp" #include "states_screens/help_screen_2.hpp" #include "states_screens/help_screen_3.hpp" @@ -66,7 +65,7 @@ void HelpScreen1::eventCallback(Widget* widget, const std::string& name, const i // Create player and associate player with keyboard StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + device, NULL); if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL) { @@ -83,7 +82,7 @@ void HelpScreen1::eventCallback(Widget* widget, const std::string& name, const i ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } else if (name == "category") @@ -108,16 +107,16 @@ void HelpScreen1::init() { Screen::init(); RibbonWidget* w = this->getWidget("category"); - ButtonWidget* tutorial = getWidget("startTutorial"); + ButtonWidget* tutorial = getWidget("startTutorial"); - if (StateManager::get()->getGameState() == GUIEngine::INGAME_MENU) - { - tutorial->setDeactivated(); - } - else - { - tutorial->setActivated(); - } + if (StateManager::get()->getGameState() == GUIEngine::INGAME_MENU) + { + tutorial->setDeactivated(); + } + else + { + tutorial->setActivated(); + } if (w != NULL) w->select( "page1", PLAYER_ID_GAME_MASTER ); } //init diff --git a/src/states_screens/help_screen_1.hpp b/src/states_screens/help_screen_1.hpp index a8750bf35..6677c6001 100644 --- a/src/states_screens/help_screen_1.hpp +++ b/src/states_screens/help_screen_1.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/help_screen_2.cpp b/src/states_screens/help_screen_2.cpp index d5194a754..f3c751474 100644 --- a/src/states_screens/help_screen_2.cpp +++ b/src/states_screens/help_screen_2.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/help_screen_2.hpp b/src/states_screens/help_screen_2.hpp index b773b733e..d7a1d75ad 100644 --- a/src/states_screens/help_screen_2.hpp +++ b/src/states_screens/help_screen_2.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/help_screen_3.cpp b/src/states_screens/help_screen_3.cpp index 8e2813297..35693dd94 100644 --- a/src/states_screens/help_screen_3.cpp +++ b/src/states_screens/help_screen_3.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/help_screen_3.hpp b/src/states_screens/help_screen_3.hpp index 904fb6f38..74a5e2258 100644 --- a/src/states_screens/help_screen_3.hpp +++ b/src/states_screens/help_screen_3.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/help_screen_4.cpp b/src/states_screens/help_screen_4.cpp index fbd75b828..97ddf3809 100644 --- a/src/states_screens/help_screen_4.cpp +++ b/src/states_screens/help_screen_4.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/help_screen_4.hpp b/src/states_screens/help_screen_4.hpp index eb4941549..8142b4540 100644 --- a/src/states_screens/help_screen_4.hpp +++ b/src/states_screens/help_screen_4.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index daac169ab..eda279bb3 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -1,6 +1,6 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 +// +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -38,6 +38,7 @@ #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" #include "modes/overworld.hpp" +#include "online/profile.hpp" #include "states_screens/race_setup_screen.hpp" #include "states_screens/state_manager.hpp" #include "utils/translation.hpp" @@ -58,8 +59,8 @@ static const char ID_DONT_USE[] = "x"; // Use '/' as special character to avoid that someone creates // a kart called 'locked' static const char ID_LOCKED[] = "locked/"; - -DEFINE_SCREEN_SINGLETON( KartSelectionScreen ); +KartSelectionScreen* KartSelectionScreen::m_instance_ptr = NULL; +//DEFINE_SCREEN_SINGLETON( KartSelectionScreen ); class PlayerKartWidget; @@ -71,66 +72,50 @@ class PlayerKartWidget; way?) */ static int g_root_id; -class FocusDispatcher : public Widget +// ------------------------------------------------------------------------ +FocusDispatcher::FocusDispatcher(KartSelectionScreen* parent) : Widget(WTYPE_BUTTON) { - KartSelectionScreen* m_parent; - int m_reserved_id; + m_parent = parent; + m_supports_multiplayer = true; + m_is_initialised = false; - bool m_is_initialised; + m_x = 0; + m_y = 0; + m_w = 1; + m_h = 1; -public: + m_reserved_id = Widget::getNewNoFocusID(); +} // FocusDispatcher +// ------------------------------------------------------------------------ +void FocusDispatcher::setRootID(const int reservedID) +{ + assert(reservedID != -1); - LEAK_CHECK() + m_reserved_id = reservedID; - // ------------------------------------------------------------------------ - FocusDispatcher(KartSelectionScreen* parent) : Widget(WTYPE_BUTTON) + if (m_element != NULL) { - m_parent = parent; - m_supports_multiplayer = true; - m_is_initialised = false; + m_element->setID(m_reserved_id); + } - m_x = 0; - m_y = 0; - m_w = 1; - m_h = 1; + m_is_initialised = true; +} // setRootID - m_reserved_id = Widget::getNewNoFocusID(); - } // FocusDispatcher - // ------------------------------------------------------------------------ - void setRootID(const int reservedID) - { - assert(reservedID != -1); +// ------------------------------------------------------------------------ +void FocusDispatcher::add() +{ + core::rect widget_size(m_x, m_y, m_x + m_w, m_y + m_h); - m_reserved_id = reservedID; + m_element = GUIEngine::getGUIEnv()->addButton(widget_size, NULL, + m_reserved_id, + L"Dispatcher", L""); - if (m_element != NULL) - { - m_element->setID(m_reserved_id); - } - - m_is_initialised = true; - } // setRootID - - // ------------------------------------------------------------------------ - virtual void add() - { - core::rect widget_size(m_x, m_y, m_x + m_w, m_y + m_h); - - m_element = GUIEngine::getGUIEnv()->addButton(widget_size, NULL, - m_reserved_id, - L"Dispatcher", L""); - - m_id = m_element->getID(); - m_element->setTabStop(true); - m_element->setTabGroup(false); - m_element->setTabOrder(m_id); - m_element->setVisible(false); - } // add - - // ------------------------------------------------------------------------ - - virtual EventPropagation focused(const int playerID); -}; // FocusDispatcher + m_id = m_element->getID(); + m_element->setTabStop(true); + m_element->setTabGroup(false); + m_element->setTabOrder(m_id); + m_element->setVisible(false); +} static FocusDispatcher* g_dispatcher = NULL; @@ -138,63 +123,53 @@ static FocusDispatcher* g_dispatcher = NULL; /** A small extension to the spinner widget to add features like player ID * management or badging */ -class PlayerNameSpinner : public SpinnerWidget + +PlayerNameSpinner::PlayerNameSpinner(KartSelectionScreen* parent, const int playerID) { - int m_playerID; - bool m_incorrect; - irr::gui::IGUIImage* m_red_mark_widget; - KartSelectionScreen* m_parent; + m_playerID = playerID; + m_incorrect = false; + m_red_mark_widget = NULL; + m_parent = parent; +} // PlayerNameSpinner +// ------------------------------------------------------------------------ +void PlayerNameSpinner::setID(const int m_playerID) +{ + PlayerNameSpinner::m_playerID = m_playerID; +} // setID +// ------------------------------------------------------------------------ +/** Add a red mark on the spinner to mean "invalid choice" */ +void PlayerNameSpinner::markAsIncorrect() +{ + if (m_incorrect) return; // already flagged as incorrect - //virtual EventPropagation focused(const int m_playerID) ; + m_incorrect = true; -public: - PlayerNameSpinner(KartSelectionScreen* parent, const int playerID) + irr::video::ITexture* texture = irr_driver->getTexture( + file_manager->getTextureFile("red_mark.png") ); + const int mark_size = m_h; + const int mark_x = m_w - mark_size*2; + const int mark_y = 0; + core::recti red_mark_area(mark_x, mark_y, mark_x + mark_size, + mark_y + mark_size); + m_red_mark_widget = GUIEngine::getGUIEnv()->addImage( red_mark_area, + /* parent */ m_element ); + m_red_mark_widget->setImage(texture); + m_red_mark_widget->setScaleImage(true); + m_red_mark_widget->setTabStop(false); + m_red_mark_widget->setUseAlphaChannel(true); +} // markAsIncorrect + +// ------------------------------------------------------------------------ +/** Remove any red mark set with 'markAsIncorrect' */ +void PlayerNameSpinner::markAsCorrect() +{ + if (m_incorrect) { - m_playerID = playerID; - m_incorrect = false; + m_red_mark_widget->remove(); m_red_mark_widget = NULL; - m_parent = parent; - } // PlayerNameSpinner - // ------------------------------------------------------------------------ - void setID(const int m_playerID) - { - PlayerNameSpinner::m_playerID = m_playerID; - } // setID - // ------------------------------------------------------------------------ - /** Add a red mark on the spinner to mean "invalid choice" */ - void markAsIncorrect() - { - if (m_incorrect) return; // already flagged as incorrect - - m_incorrect = true; - - irr::video::ITexture* texture = irr_driver->getTexture( - file_manager->getTextureFile("red_mark.png") ); - const int mark_size = m_h; - const int mark_x = m_w - mark_size*2; - const int mark_y = 0; - core::recti red_mark_area(mark_x, mark_y, mark_x + mark_size, - mark_y + mark_size); - m_red_mark_widget = GUIEngine::getGUIEnv()->addImage( red_mark_area, - /* parent */ m_element ); - m_red_mark_widget->setImage(texture); - m_red_mark_widget->setScaleImage(true); - m_red_mark_widget->setTabStop(false); - m_red_mark_widget->setUseAlphaChannel(true); - } // markAsIncorrect - - // ------------------------------------------------------------------------ - /** Remove any red mark set with 'markAsIncorrect' */ - void markAsCorrect() - { - if (m_incorrect) - { - m_red_mark_widget->remove(); - m_red_mark_widget = NULL; - m_incorrect = false; - } - } // markAsCorrect -}; + m_incorrect = false; + } +} // markAsCorrect // ============================================================================ @@ -205,297 +180,258 @@ public: /** A widget representing the kart selection for a player (i.e. the player's * number, name, the kart view, the kart's name) */ -class PlayerKartWidget : public Widget, - public SpinnerWidget::ISpinnerConfirmListener + +PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent, + StateManager::ActivePlayer* associatedPlayer, + Online::Profile* associatedUser, + core::recti area, const int playerID, + std::string kartGroup, + const int irrlichtWidgetID) : Widget(WTYPE_DIV) { - /** Whether this player confirmed their selection */ - bool m_ready; - - /** widget coordinates */ - int player_id_x, player_id_y, player_id_w, player_id_h; - int player_name_x, player_name_y, player_name_w, player_name_h; - int model_x, model_y, model_w, model_h; - int kart_name_x, kart_name_y, kart_name_w, kart_name_h; - - /** A reserved ID for this widget if any, -1 otherwise. (If no ID is - * reserved, widget will not be in the regular tabbing order */ - int m_irrlicht_widget_ID; - - /** For animation purposes (see method 'move') */ - int target_x, target_y, target_w, target_h; - float x_speed, y_speed, w_speed, h_speed; - - /** Object representing this player */ - StateManager::ActivePlayer* m_associatedPlayer; - int m_playerID; - - /** Internal name of the spinner; useful to interpret spinner events, - * which contain the name of the activated object */ - std::string spinnerID; - -#ifdef DEBUG - long m_magic_number; -#endif - -public: - - LEAK_CHECK() - - /** Sub-widgets created by this widget */ - //LabelWidget* m_player_ID_label; - PlayerNameSpinner* m_player_ident_spinner; - ModelViewWidget* m_model_view; - LabelWidget* m_kart_name; - - KartSelectionScreen* m_parent_screen; - - gui::IGUIStaticText* m_ready_text; - - //LabelWidget *getPlayerIDLabel() {return m_player_ID_label;} - core::stringw deviceName; - std::string m_kartInternalName; - - bool m_not_updated_yet; - - PlayerKartWidget(KartSelectionScreen* parent, - StateManager::ActivePlayer* associatedPlayer, - core::recti area, const int playerID, - std::string kartGroup, - const int irrlichtWidgetID=-1) : Widget(WTYPE_DIV) - { #ifdef DEBUG + if (associatedPlayer) assert(associatedPlayer->ok()); - m_magic_number = 0x33445566; + m_magic_number = 0x33445566; #endif - m_ready_text = NULL; - m_parent_screen = parent; + m_ready_text = NULL; + m_parent_screen = parent; - m_associatedPlayer = associatedPlayer; - x_speed = 1.0f; - y_speed = 1.0f; - w_speed = 1.0f; - h_speed = 1.0f; - m_ready = false; - m_not_updated_yet = true; + m_associated_user = associatedUser; + m_associatedPlayer = associatedPlayer; + x_speed = 1.0f; + y_speed = 1.0f; + w_speed = 1.0f; + h_speed = 1.0f; + m_ready = false; + m_not_updated_yet = true; - m_irrlicht_widget_ID = irrlichtWidgetID; + m_irrlicht_widget_ID = irrlichtWidgetID; - m_playerID = playerID; - m_properties[PROP_ID] = StringUtils::insertValues("@p%i", m_playerID); + m_playerID = playerID; + m_properties[PROP_ID] = StringUtils::insertValues("@p%i", m_playerID); - setSize(area.UpperLeftCorner.X, area.UpperLeftCorner.Y, - area.getWidth(), area.getHeight() ); - target_x = m_x; - target_y = m_y; - target_w = m_w; - target_h = m_h; + setSize(area.UpperLeftCorner.X, area.UpperLeftCorner.Y, + area.getWidth(), area.getHeight() ); + target_x = m_x; + target_y = m_y; + target_w = m_w; + target_h = m_h; - // ---- Player identity spinner - m_player_ident_spinner = new PlayerNameSpinner(parent, m_playerID); - m_player_ident_spinner->m_x = player_name_x; - m_player_ident_spinner->m_y = player_name_y; - m_player_ident_spinner->m_w = player_name_w; - m_player_ident_spinner->m_h = player_name_h; + // ---- Player identity spinner + m_player_ident_spinner = NULL; - if (parent->m_multiplayer) + m_player_ident_spinner = new PlayerNameSpinner(parent, m_playerID); + m_player_ident_spinner->m_x = player_name_x; + m_player_ident_spinner->m_y = player_name_y; + m_player_ident_spinner->m_w = player_name_w; + m_player_ident_spinner->m_h = player_name_h; + + if (parent->m_multiplayer && associatedPlayer) + { + if (associatedPlayer->getDevice()->getType() == DT_KEYBOARD) { - if (associatedPlayer->getDevice()->getType() == DT_KEYBOARD) - { - m_player_ident_spinner->setBadge(KEYBOARD_BADGE); - } - else if (associatedPlayer->getDevice()->getType() == DT_GAMEPAD) - { - m_player_ident_spinner->setBadge(GAMEPAD_BADGE); - } + m_player_ident_spinner->setBadge(KEYBOARD_BADGE); } - - if (irrlichtWidgetID == -1) + else if (associatedPlayer->getDevice()->getType() == DT_GAMEPAD) { - m_player_ident_spinner->m_tab_down_root = g_root_id; + m_player_ident_spinner->setBadge(GAMEPAD_BADGE); } + } + else if (m_associated_user) // online user, FIXME is that useful ? + { + m_player_ident_spinner->setBadge(OK_BADGE); + } - spinnerID = StringUtils::insertValues("@p%i_spinner", m_playerID); + if (irrlichtWidgetID == -1) + { + m_player_ident_spinner->m_tab_down_root = g_root_id; + } - m_player_ident_spinner->m_properties[PROP_ID] = spinnerID; - if (parent->m_multiplayer) + spinnerID = StringUtils::insertValues("@p%i_spinner", m_playerID); + + m_player_ident_spinner->m_properties[PROP_ID] = spinnerID; + if (parent->m_multiplayer) + { + const int playerAmount = UserConfigParams::m_all_players.size(); + m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; + m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = + StringUtils::toString(playerAmount-1); + m_player_ident_spinner->m_properties[PROP_WRAP_AROUND] = "true"; + } + else + { + m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; + m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = "0"; + } + + //m_player_ident_spinner->m_event_handler = this; + m_children.push_back(m_player_ident_spinner); + + + // ----- Kart model view + m_model_view = new ModelViewWidget(); + + m_model_view->m_x = model_x; + m_model_view->m_y = model_y; + m_model_view->m_w = model_w; + m_model_view->m_h = model_h; + m_model_view->m_properties[PROP_ID] = + StringUtils::insertValues("@p%i_model", m_playerID); + //m_model_view->setParent(this); + m_children.push_back(m_model_view); + + // Init kart model + const std::string default_kart = UserConfigParams::m_default_kart; + const KartProperties* props = + kart_properties_manager->getKart(default_kart); + + if(!props) + { + // If the default kart can't be found (e.g. previously a addon + // kart was used, but the addon package was removed), use the + // first kart as a default. This way we don't have to hardcode + // any kart names. + int id = kart_properties_manager->getKartByGroup(kartGroup, 0); + if (id == -1) { - const int playerAmount = UserConfigParams::m_all_players.size(); - m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; - m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = - StringUtils::toString(playerAmount-1); - m_player_ident_spinner->m_properties[PROP_WRAP_AROUND] = "true"; + props = kart_properties_manager->getKartById(0); } else { - m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; - m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = "0"; + props = kart_properties_manager->getKartById(id); } - //m_player_ident_spinner->m_event_handler = this; - m_children.push_back(m_player_ident_spinner); - - - // ----- Kart model view - m_model_view = new ModelViewWidget(); - - m_model_view->m_x = model_x; - m_model_view->m_y = model_y; - m_model_view->m_w = model_w; - m_model_view->m_h = model_h; - m_model_view->m_properties[PROP_ID] = - StringUtils::insertValues("@p%i_model", m_playerID); - //m_model_view->setParent(this); - m_children.push_back(m_model_view); - - // Init kart model - const std::string default_kart = UserConfigParams::m_default_kart; - const KartProperties* props = - kart_properties_manager->getKart(default_kart); - if(!props) { - // If the default kart can't be found (e.g. previously a addon - // kart was used, but the addon package was removed), use the - // first kart as a default. This way we don't have to hardcode - // any kart names. - int id = kart_properties_manager->getKartByGroup(kartGroup, 0); - if (id == -1) - { - props = kart_properties_manager->getKartById(0); - } - else - { - props = kart_properties_manager->getKartById(id); - } - - if(!props) - { - fprintf(stderr, - "[KartSelectionScreen] WARNING: Can't find default " - "kart '%s' nor any other kart.\n", - default_kart.c_str()); - exit(-1); - } + fprintf(stderr, + "[KartSelectionScreen] WARNING: Can't find default " + "kart '%s' nor any other kart.\n", + default_kart.c_str()); + exit(-1); } - m_kartInternalName = props->getIdent(); + } + m_kartInternalName = props->getIdent(); - const KartModel &kart_model = props->getMasterKartModel(); + const KartModel &kart_model = props->getMasterKartModel(); - m_model_view->addModel( kart_model.getModel(), Vec3(0,0,0), - Vec3(35.0f, 35.0f, 35.0f), - kart_model.getBaseFrame() ); - m_model_view->addModel( kart_model.getWheelModel(0), - kart_model.getWheelGraphicsPosition(0) ); - m_model_view->addModel( kart_model.getWheelModel(1), - kart_model.getWheelGraphicsPosition(1) ); - m_model_view->addModel( kart_model.getWheelModel(2), - kart_model.getWheelGraphicsPosition(2) ); - m_model_view->addModel( kart_model.getWheelModel(3), - kart_model.getWheelGraphicsPosition(3) ); - for(size_t i=0 ; i < kart_model.getSpeedWeightedObjectsCount() ; i++) - { - const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); - m_model_view->addModel(obj.m_model, obj.m_position); - } - m_model_view->setRotateContinuously( 35.0f ); - - // ---- Kart name label - m_kart_name = new LabelWidget(); - m_kart_name->setText(props->getName(), false); - m_kart_name->m_properties[PROP_TEXT_ALIGN] = "center"; - m_kart_name->m_properties[PROP_ID] = - StringUtils::insertValues("@p%i_kartname", m_playerID); - m_kart_name->m_x = kart_name_x; - m_kart_name->m_y = kart_name_y; - m_kart_name->m_w = kart_name_w; - m_kart_name->m_h = kart_name_h; - //m_kart_name->setParent(this); - m_children.push_back(m_kart_name); - } // PlayerKartWidget - // ------------------------------------------------------------------------ - - ~PlayerKartWidget() + m_model_view->addModel( kart_model.getModel(), Vec3(0,0,0), + Vec3(35.0f, 35.0f, 35.0f), + kart_model.getBaseFrame() ); + m_model_view->addModel( kart_model.getWheelModel(0), + kart_model.getWheelGraphicsPosition(0) ); + m_model_view->addModel( kart_model.getWheelModel(1), + kart_model.getWheelGraphicsPosition(1) ); + m_model_view->addModel( kart_model.getWheelModel(2), + kart_model.getWheelGraphicsPosition(2) ); + m_model_view->addModel( kart_model.getWheelModel(3), + kart_model.getWheelGraphicsPosition(3) ); + for(size_t i=0 ; i < kart_model.getSpeedWeightedObjectsCount() ; i++) { - if (GUIEngine::getFocusForPlayer(m_playerID) == this) + const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); + m_model_view->addModel(obj.m_model, obj.m_position); + } + m_model_view->setRotateContinuously( 35.0f ); + + // ---- Kart name label + m_kart_name = new LabelWidget(); + m_kart_name->add(); // add the widget + m_kart_name->setText(props->getName(), false); + m_kart_name->m_properties[PROP_TEXT_ALIGN] = "center"; + m_kart_name->m_properties[PROP_ID] = + StringUtils::insertValues("@p%i_kartname", m_playerID); + m_kart_name->m_x = kart_name_x; + m_kart_name->m_y = kart_name_y; + m_kart_name->m_w = kart_name_w; + m_kart_name->m_h = kart_name_h; + //m_kart_name->setParent(this); + m_children.push_back(m_kart_name); +} // PlayerKartWidget +// ------------------------------------------------------------------------ + +PlayerKartWidget::~PlayerKartWidget() +{ + if (GUIEngine::getFocusForPlayer(m_playerID) == this) + { + GUIEngine::focusNothingForPlayer(m_playerID); + } + + //if (m_player_ID_label->getIrrlichtElement() != NULL) + // m_player_ID_label->getIrrlichtElement()->remove(); + + if (m_player_ident_spinner != NULL) + { + m_player_ident_spinner->setListener(NULL); + + if (m_player_ident_spinner->getIrrlichtElement() != NULL) { - GUIEngine::focusNothingForPlayer(m_playerID); + m_player_ident_spinner->getIrrlichtElement()->remove(); } + } - //if (m_player_ID_label->getIrrlichtElement() != NULL) - // m_player_ID_label->getIrrlichtElement()->remove(); + if (m_model_view->getIrrlichtElement() != NULL) + m_model_view->getIrrlichtElement()->remove(); - if (m_player_ident_spinner != NULL) - { - m_player_ident_spinner->setListener(NULL); + if (m_kart_name->getIrrlichtElement() != NULL) + m_kart_name->getIrrlichtElement()->remove(); - if (m_player_ident_spinner->getIrrlichtElement() != NULL) - { - m_player_ident_spinner->getIrrlichtElement()->remove(); - } - } - - if (m_model_view->getIrrlichtElement() != NULL) - m_model_view->getIrrlichtElement()->remove(); - - if (m_kart_name->getIrrlichtElement() != NULL) - m_kart_name->getIrrlichtElement()->remove(); - - getCurrentScreen()->manualRemoveWidget(this); + getCurrentScreen()->manualRemoveWidget(this); #ifdef DEBUG - m_magic_number = 0xDEADBEEF; + m_magic_number = 0xDEADBEEF; #endif - } // ~PlayerKartWidget +} // ~PlayerKartWidget - // ------------------------------------------------------------------------ - /** Called when players are renumbered (changes the player ID) */ - void setPlayerID(const int newPlayerID) - { - assert(m_magic_number == 0x33445566); +// ------------------------------------------------------------------------ +/** Called when players are renumbered (changes the player ID) */ +void PlayerKartWidget::setPlayerID(const int newPlayerID) +{ + assert(m_magic_number == 0x33445566); - if (StateManager::get()->getActivePlayer(newPlayerID) + if (StateManager::get()->getActivePlayer(newPlayerID) != m_associatedPlayer) - { - std::cerr << "[KartSelectionScreen] WARNING: Internal " - "inconsistency, PlayerKartWidget has IDs and " - "pointers that do not correspond to one player\n"; - fprintf(stderr, - " Player: %p - Index: %d - m_associatedPlayer: %p\n", - StateManager::get()->getActivePlayer(newPlayerID), - newPlayerID, m_associatedPlayer); - assert(false); - } - - // Remove current focus, but rembmer it - Widget* focus = GUIEngine::getFocusForPlayer(m_playerID); - GUIEngine::focusNothingForPlayer(m_playerID); - - // Change the player ID - m_playerID = newPlayerID; - - // restore previous focus, but with new player ID - if (focus != NULL) focus->setFocusForPlayer(m_playerID); - - if (m_player_ident_spinner != NULL) - m_player_ident_spinner->setID(m_playerID); - } // setPlayerID - - // ------------------------------------------------------------------------ - /** Returns the ID of this player */ - int getPlayerID() const { - assert(m_magic_number == 0x33445566); - return m_playerID; - } // getPlayerID + std::cerr << "[KartSelectionScreen] WARNING: Internal " + "inconsistency, PlayerKartWidget has IDs and " + "pointers that do not correspond to one player\n"; + fprintf(stderr, + " Player: %p - Index: %d - m_associatedPlayer: %p\n", + StateManager::get()->getActivePlayer(newPlayerID), + newPlayerID, m_associatedPlayer); + assert(false); + } - // ------------------------------------------------------------------------ - /** Add the widgets to the current screen */ - virtual void add() + // Remove current focus, but rembmer it + Widget* focus = GUIEngine::getFocusForPlayer(m_playerID); + GUIEngine::focusNothingForPlayer(m_playerID); + + // Change the player ID + m_playerID = newPlayerID; + + // restore previous focus, but with new player ID + if (focus != NULL) focus->setFocusForPlayer(m_playerID); + + if (m_player_ident_spinner != NULL) + m_player_ident_spinner->setID(m_playerID); +} // setPlayerID + +// ------------------------------------------------------------------------ +/** Returns the ID of this player */ +int PlayerKartWidget::getPlayerID() const +{ + assert(m_magic_number == 0x33445566); + return m_playerID; +} // getPlayerID + +// ------------------------------------------------------------------------ +/** Add the widgets to the current screen */ +void PlayerKartWidget::add() +{ + assert(m_magic_number == 0x33445566); + + assert(KartSelectionScreen::getRunningInstance() + ->m_kart_widgets.contains(this)); + if (m_associatedPlayer) // if player is local { - assert(m_magic_number == 0x33445566); - - assert(KartSelectionScreen::getInstance() - ->m_kart_widgets.contains(this)); bool mineInList = false; for (int p=0; pactivePlayerCount(); p++) { @@ -508,341 +444,346 @@ public: } } assert(mineInList); + } - //m_player_ID_label->add(); + //m_player_ID_label->add(); - // the first player will have an ID of its own to allow for keyboard - // navigation despite this widget being added last - if (m_irrlicht_widget_ID != -1) - m_player_ident_spinner->m_reserved_id = m_irrlicht_widget_ID; - else - m_player_ident_spinner->m_reserved_id = Widget::getNewNoFocusID(); + // the first player will have an ID of its own to allow for keyboard + // navigation despite this widget being added last + if (m_irrlicht_widget_ID != -1) + m_player_ident_spinner->m_reserved_id = m_irrlicht_widget_ID; + else + m_player_ident_spinner->m_reserved_id = Widget::getNewNoFocusID(); - m_player_ident_spinner->add(); - m_player_ident_spinner->getIrrlichtElement()->setTabStop(false); - m_player_ident_spinner->setListener(this); + m_player_ident_spinner->add(); + m_player_ident_spinner->getIrrlichtElement()->setTabStop(false); + m_player_ident_spinner->setListener(this); - m_model_view->add(); - m_kart_name->add(); + m_model_view->add(); + m_kart_name->add(); - m_model_view->update(0); + m_model_view->update(0); - m_player_ident_spinner->clearLabels(); - if (m_parent_screen->m_multiplayer) + m_player_ident_spinner->clearLabels(); + + irr::core::stringw name; // name of the player + if (m_associatedPlayer) + name = m_associatedPlayer->getProfile()->getName(); + if (m_associated_user) + name = m_associated_user->getUserName(); + + if (m_parent_screen->m_multiplayer) + { + const int playerAmount = UserConfigParams::m_all_players.size(); + for (int n=0; naddLabel( translations->fribidize(name) ); - } - - // select the right player profile in the spinner - m_player_ident_spinner->setValue(m_associatedPlayer->getProfile() - ->getName() ); - } - else - { - m_player_ident_spinner->addLabel( m_associatedPlayer->getProfile()->getName() ); - m_player_ident_spinner->setVisible(false); + core::stringw name = UserConfigParams::m_all_players[n].getName(); + m_player_ident_spinner->addLabel( translations->fribidize(name) ); } - assert(m_player_ident_spinner->getStringValue() == - m_associatedPlayer->getProfile()->getName()); - } // add - - // ------------------------------------------------------------------------ - /** Get the associated ActivePlayer object*/ - StateManager::ActivePlayer* getAssociatedPlayer() + // select the right player profile in the spinner + m_player_ident_spinner->setValue(name); + } + else { - assert(m_magic_number == 0x33445566); - return m_associatedPlayer; - } // getAssociatedPlayer + m_player_ident_spinner->addLabel(name); + m_player_ident_spinner->setVisible(false); + } - // ------------------------------------------------------------------------ - /** Starts a 'move/resize' animation, by simply passing destination coords. - * The animation will then occur on each call to 'onUpdate'. */ - void move(const int x, const int y, const int w, const int h) - { - assert(m_magic_number == 0x33445566); - target_x = x; - target_y = y; - target_w = w; - target_h = h; + assert(m_player_ident_spinner->getStringValue() == name); +} // add - x_speed = abs( m_x - x ) / 300.0f; - y_speed = abs( m_y - y ) / 300.0f; - w_speed = abs( m_w - w ) / 300.0f; - h_speed = abs( m_h - h ) / 300.0f; - } // move +// ------------------------------------------------------------------------ +/** Get the associated ActivePlayer object*/ +StateManager::ActivePlayer* PlayerKartWidget::getAssociatedPlayer() +{ + assert(m_magic_number == 0x33445566); + return m_associatedPlayer; +} // getAssociatedPlayer - // ------------------------------------------------------------------------ - /** Call when player confirmed his identity and kart */ - void markAsReady() - { - assert(m_magic_number == 0x33445566); - if (m_ready) return; // already ready +// ------------------------------------------------------------------------ +/** Starts a 'move/resize' animation, by simply passing destination coords. + * The animation will then occur on each call to 'onUpdate'. */ +void PlayerKartWidget::move(const int x, const int y, const int w, const int h) +{ + assert(m_magic_number == 0x33445566); + target_x = x; + target_y = y; + target_w = w; + target_h = h; - m_ready = true; + x_speed = abs( m_x - x ) / 300.0f; + y_speed = abs( m_y - y ) / 300.0f; + w_speed = abs( m_w - w ) / 300.0f; + h_speed = abs( m_h - h ) / 300.0f; +} // move - stringw playerNameString = m_player_ident_spinner->getStringValue(); - core::rect rect(core::position2di(m_player_ident_spinner->m_x, - m_player_ident_spinner->m_y), - core::dimension2di(m_player_ident_spinner->m_w, - m_player_ident_spinner->m_h)); - // 'playerNameString' is already fribidize, so we need to use - // 'insertValues' and not _("...", a) so it's not flipped again - m_ready_text = - GUIEngine::getGUIEnv()->addStaticText( - StringUtils::insertValues(_("%s is ready"), - playerNameString).c_str(), - rect ); - m_ready_text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER ); +// ------------------------------------------------------------------------ +/** Call when player confirmed his identity and kart */ +void PlayerKartWidget::markAsReady() +{ + assert(m_magic_number == 0x33445566); + if (m_ready) return; // already ready - m_children.remove(m_player_ident_spinner); - m_player_ident_spinner->setListener(NULL); - m_player_ident_spinner->getIrrlichtElement()->remove(); - m_player_ident_spinner->elementRemoved(); - delete m_player_ident_spinner; - m_player_ident_spinner = NULL; + m_ready = true; - sfx_manager->quickSound( "wee" ); + stringw playerNameString = m_player_ident_spinner->getStringValue(); + core::rect rect(core::position2di(m_player_ident_spinner->m_x, + m_player_ident_spinner->m_y), + core::dimension2di(m_player_ident_spinner->m_w, + m_player_ident_spinner->m_h)); + // 'playerNameString' is already fribidize, so we need to use + // 'insertValues' and not _("...", a) so it's not flipped again + m_ready_text = + GUIEngine::getGUIEnv()->addStaticText( + StringUtils::insertValues(_("%s is ready"), + playerNameString).c_str(), + rect ); + m_ready_text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER ); - m_model_view->setRotateTo(30.0f, 1.0f); + m_children.remove(m_player_ident_spinner); + m_player_ident_spinner->setListener(NULL); + m_player_ident_spinner->getIrrlichtElement()->remove(); + m_player_ident_spinner->elementRemoved(); + delete m_player_ident_spinner; + m_player_ident_spinner = NULL; - player_id_w *= 2; - player_name_w = 0; + sfx_manager->quickSound( "wee" ); - m_model_view->setBadge(OK_BADGE); - } // markAsReady + m_model_view->setRotateTo(30.0f, 1.0f); - // ------------------------------------------------------------------------ - /** \return Whether this player confirmed his kart and indent selection */ - bool isReady() - { - assert(m_magic_number == 0x33445566); - return m_ready; - } // isReady + player_id_w *= 2; + player_name_w = 0; - // ------------------------------------------------------------------------- - /** Updates the animation (moving/shrinking/etc.) */ - void onUpdate(float delta) - { - assert(m_magic_number == 0x33445566); - if (target_x == m_x && target_y == m_y && + m_model_view->setBadge(OK_BADGE); +} // markAsReady + +// ------------------------------------------------------------------------ +/** \return Whether this player confirmed his kart and indent selection */ +bool PlayerKartWidget::isReady() +{ + assert(m_magic_number == 0x33445566); + return m_ready; +} // isReady + +// ------------------------------------------------------------------------- +/** Updates the animation (moving/shrinking/etc.) */ +void PlayerKartWidget::onUpdate(float delta) +{ + assert(m_magic_number == 0x33445566); + if (target_x == m_x && target_y == m_y && target_w == m_w && target_h == m_h) return; - int move_step = (int)(delta*1000.0f); + int move_step = (int)(delta*1000.0f); - // move x towards target - if (m_x < target_x) - { - m_x += (int)(move_step*x_speed); - // don't move to the other side of the target - if (m_x > target_x) m_x = target_x; - } - else if (m_x > target_x) - { - m_x -= (int)(move_step*x_speed); - // don't move to the other side of the target - if (m_x < target_x) m_x = target_x; - } - - // move y towards target - if (m_y < target_y) - { - m_y += (int)(move_step*y_speed); - // don't move to the other side of the target - if (m_y > target_y) m_y = target_y; - } - else if (m_y > target_y) - { - m_y -= (int)(move_step*y_speed); - // don't move to the other side of the target - if (m_y < target_y) m_y = target_y; - } - - // move w towards target - if (m_w < target_w) - { - m_w += (int)(move_step*w_speed); - // don't move to the other side of the target - if (m_w > target_w) m_w = target_w; - } - else if (m_w > target_w) - { - m_w -= (int)(move_step*w_speed); - // don't move to the other side of the target - if (m_w < target_w) m_w = target_w; - } - // move h towards target - if (m_h < target_h) - { - m_h += (int)(move_step*h_speed); - // don't move to the other side of the target - if (m_h > target_h) m_h = target_h; - } - else if (m_h > target_h) - { - m_h -= (int)(move_step*h_speed); - // don't move to the other side of the target - if (m_h < target_h) m_h = target_h; - } - - setSize(m_x, m_y, m_w, m_h); - - if (m_player_ident_spinner != NULL) - { - m_player_ident_spinner->move(player_name_x, - player_name_y, - player_name_w, - player_name_h ); - } - if (m_ready_text != NULL) - { - m_ready_text->setRelativePosition( - core::recti(core::position2di(player_name_x, player_name_y), - core::dimension2di(player_name_w, player_name_h)) ); - } - - m_model_view->move(model_x, - model_y, - model_w, - model_h); - m_kart_name->move(kart_name_x, - kart_name_y, - kart_name_w, - kart_name_h); - - // When coming from the overworld, we must rebuild the preview scene at - // least once, since the scene is being cleared by leaving the overworld - if (m_not_updated_yet) - { - m_model_view->clearRttProvider(); - m_not_updated_yet = false; - } - } // onUpdate - - // ------------------------------------------------------------------------- - /** Event callback */ - virtual GUIEngine::EventPropagation transmitEvent( - Widget* w, - const std::string& originator, - const int m_playerID) + // move x towards target + if (m_x < target_x) { - assert(m_magic_number == 0x33445566); - // if it's declared ready, there is really nothing to process - if (m_ready) return EVENT_LET; + m_x += (int)(move_step*x_speed); + // don't move to the other side of the target + if (m_x > target_x) m_x = target_x; + } + else if (m_x > target_x) + { + m_x -= (int)(move_step*x_speed); + // don't move to the other side of the target + if (m_x < target_x) m_x = target_x; + } - //std::cout << "= kart selection :: transmitEvent " - // << originator << " =\n"; + // move y towards target + if (m_y < target_y) + { + m_y += (int)(move_step*y_speed); + // don't move to the other side of the target + if (m_y > target_y) m_y = target_y; + } + else if (m_y > target_y) + { + m_y -= (int)(move_step*y_speed); + // don't move to the other side of the target + if (m_y < target_y) m_y = target_y; + } - std::string name = w->m_properties[PROP_ID]; + // move w towards target + if (m_w < target_w) + { + m_w += (int)(move_step*w_speed); + // don't move to the other side of the target + if (m_w > target_w) m_w = target_w; + } + else if (m_w > target_w) + { + m_w -= (int)(move_step*w_speed); + // don't move to the other side of the target + if (m_w < target_w) m_w = target_w; + } + // move h towards target + if (m_h < target_h) + { + m_h += (int)(move_step*h_speed); + // don't move to the other side of the target + if (m_h > target_h) m_h = target_h; + } + else if (m_h > target_h) + { + m_h -= (int)(move_step*h_speed); + // don't move to the other side of the target + if (m_h < target_h) m_h = target_h; + } - //std::cout << " (checking if that's me: I am " - // << spinnerID << ")\n"; + setSize(m_x, m_y, m_w, m_h); - // update player profile when spinner changed - if (originator == spinnerID) + if (m_player_ident_spinner != NULL) + { + m_player_ident_spinner->move(player_name_x, + player_name_y, + player_name_w, + player_name_h ); + } + if (m_ready_text != NULL) + { + m_ready_text->setRelativePosition( + core::recti(core::position2di(player_name_x, player_name_y), + core::dimension2di(player_name_w, player_name_h)) ); + } + + m_model_view->move(model_x, + model_y, + model_w, + model_h); + m_kart_name->move(kart_name_x, + kart_name_y, + kart_name_w, + kart_name_h); + + // When coming from the overworld, we must rebuild the preview scene at + // least once, since the scene is being cleared by leaving the overworld + if (m_not_updated_yet) + { + m_model_view->clearRttProvider(); + m_not_updated_yet = false; + } +} // onUpdate + +// ------------------------------------------------------------------------- +/** Event callback */ +GUIEngine::EventPropagation PlayerKartWidget::transmitEvent( + Widget* w, + const std::string& originator, + const int m_playerID) +{ + assert(m_magic_number == 0x33445566); + // if it's declared ready, there is really nothing to process + if (m_ready) return EVENT_LET; + + //std::cout << "= kart selection :: transmitEvent " + // << originator << " =\n"; + + std::string name = w->m_properties[PROP_ID]; + + //std::cout << " (checking if that's me: I am " + // << spinnerID << ")\n"; + + // update player profile when spinner changed + if (originator == spinnerID) + { + if(UserConfigParams::logGUI()) { - if(UserConfigParams::logGUI()) - { - std::cout << "[KartSelectionScreen] Identity changed " - "for player " << m_playerID - << " : " << irr::core::stringc( - m_player_ident_spinner->getStringValue() - .c_str()).c_str() - << std::endl; - } - - if (m_parent_screen->m_multiplayer) - { - m_associatedPlayer->setPlayerProfile( - UserConfigParams::m_all_players.get(m_player_ident_spinner - ->getValue()) ); - } + std::cout << "[KartSelectionScreen] Identity changed " + "for player " << m_playerID + << " : " << irr::core::stringc( + m_player_ident_spinner->getStringValue() + .c_str()).c_str() + << std::endl; } - return EVENT_LET; // continue propagating the event - } // transmitEvent - - // ------------------------------------------------------------------------- - /** Sets the size of the widget as a whole, and placed children widgets - * inside itself */ - void setSize(const int x, const int y, const int w, const int h) - { - assert(m_magic_number == 0x33445566); - m_x = x; - m_y = y; - m_w = w; - m_h = h; - - // -- sizes - player_id_w = w; - player_id_h = GUIEngine::getFontHeight(); - - player_name_h = 40; - player_name_w = std::min(400, w); - - kart_name_w = w; - kart_name_h = 25; - - // for shrinking effect - if (h < 175) + if (m_parent_screen->m_multiplayer) { - const float factor = h / 175.0f; - kart_name_h = (int)(kart_name_h*factor); - player_name_h = (int)(player_name_h*factor); - player_id_h = (int)(player_id_h*factor); + m_associatedPlayer->setPlayerProfile( + UserConfigParams::m_all_players.get(m_player_ident_spinner + ->getValue()) ); } + } - // --- layout - player_id_x = x; - player_id_y = y; + return EVENT_LET; // continue propagating the event +} // transmitEvent - player_name_x = x + w/2 - player_name_w/2; - player_name_y = y + player_id_h; +// ------------------------------------------------------------------------- +/** Sets the size of the widget as a whole, and placed children widgets + * inside itself */ +void PlayerKartWidget::setSize(const int x, const int y, const int w, const int h) +{ + assert(m_magic_number == 0x33445566); + m_x = x; + m_y = y; + m_w = w; + m_h = h; - const int modelMaxHeight = h - kart_name_h - player_name_h - - player_id_h; - const int modelMaxWidth = w; - const int bestSize = std::min(modelMaxWidth, modelMaxHeight); - const int modelY = y + player_name_h + player_id_h; - model_x = x + w/2 - (int)(bestSize/2); - model_y = modelY + modelMaxHeight/2 - bestSize/2; - model_w = (int)(bestSize); - model_h = bestSize; + // -- sizes + player_id_w = w; + player_id_h = GUIEngine::getFontHeight(); - kart_name_x = x; - kart_name_y = y + h - kart_name_h; - } // setSize + player_name_h = 40; + player_name_w = std::min(400, w); - // ------------------------------------------------------------------------- + kart_name_w = w; + kart_name_h = 25; - /** Sets which kart was selected for this player */ - void setKartInternalName(const std::string& whichKart) + // for shrinking effect + if (h < 175) { - assert(m_magic_number == 0x33445566); - m_kartInternalName = whichKart; - } // setKartInternalName + const float factor = h / 175.0f; + kart_name_h = (int)(kart_name_h*factor); + player_name_h = (int)(player_name_h*factor); + player_id_h = (int)(player_id_h*factor); + } - // ------------------------------------------------------------------------- + // --- layout + player_id_x = x; + player_id_y = y; - const std::string& getKartInternalName() const - { - assert(m_magic_number == 0x33445566); - return m_kartInternalName; - } // getKartInternalName + player_name_x = x + w/2 - player_name_w/2; + player_name_y = y + player_id_h; - // ------------------------------------------------------------------------- + const int modelMaxHeight = h - kart_name_h - player_name_h + - player_id_h; + const int modelMaxWidth = w; + const int bestSize = std::min(modelMaxWidth, modelMaxHeight); + const int modelY = y + player_name_h + player_id_h; + model_x = x + w/2 - (int)(bestSize/2); + model_y = modelY + modelMaxHeight/2 - bestSize/2; + model_w = (int)(bestSize); + model_h = bestSize; - /** \brief Event callback from ISpinnerConfirmListener */ - virtual EventPropagation onSpinnerConfirmed() - { - KartSelectionScreen::getInstance()->playerConfirm(m_playerID); - return EVENT_BLOCK; - } // onSpinnerConfirmed -}; // PlayerKartWidget + kart_name_x = x; + kart_name_y = y + h - kart_name_h; +} // setSize + +// ------------------------------------------------------------------------- + +/** Sets which kart was selected for this player */ +void PlayerKartWidget::setKartInternalName(const std::string& whichKart) +{ + assert(m_magic_number == 0x33445566); + m_kartInternalName = whichKart; +} // setKartInternalName + +// ------------------------------------------------------------------------- + +const std::string& PlayerKartWidget::getKartInternalName() const +{ + assert(m_magic_number == 0x33445566); + return m_kartInternalName; +} // getKartInternalName + +// ------------------------------------------------------------------------- + +/** \brief Event callback from ISpinnerConfirmListener */ +EventPropagation PlayerKartWidget::onSpinnerConfirmed() +{ + KartSelectionScreen::getRunningInstance()->playerConfirm(m_playerID); + return EVENT_BLOCK; +} // onSpinnerConfirmed /** Small utility function that returns whether the two given players chose * the same kart. The advantage of this function is that it can handle @@ -850,7 +791,7 @@ public: bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2) { return player1.getKartInternalName() == player2.getKartInternalName() && - player1.getKartInternalName() != RANDOM_KART_ID; + player1.getKartInternalName() != RANDOM_KART_ID; } #if 0 @@ -859,128 +800,48 @@ bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2) #endif // ============================================================================ -class KartHoverListener : public DynamicRibbonHoverListener +KartHoverListener::KartHoverListener(KartSelectionScreen* parent) { - KartSelectionScreen* m_parent; -public: - unsigned int m_magic_number; + m_magic_number = 0xCAFEC001; + m_parent = parent; +} // KartHoverListener - KartHoverListener(KartSelectionScreen* parent) +// ------------------------------------------------------------------------ +KartHoverListener::~KartHoverListener() +{ + assert(m_magic_number == 0xCAFEC001); + m_magic_number = 0xDEADBEEF; +} // ~KartHoverListener + +// ------------------------------------------------------------------------ +void KartHoverListener::onSelectionChanged(DynamicRibbonWidget* theWidget, + const std::string& selectionID, + const irr::core::stringw& selectionText, + const int playerID) +{ + assert(m_magic_number == 0xCAFEC001); + + // Don't allow changing the selection after confirming it + if (m_parent->m_kart_widgets[playerID].isReady()) { - m_magic_number = 0xCAFEC001; - m_parent = parent; - } // KartHoverListener - - // ------------------------------------------------------------------------ - virtual ~KartHoverListener() - { - assert(m_magic_number == 0xCAFEC001); - m_magic_number = 0xDEADBEEF; - } // ~KartHoverListener - - // ------------------------------------------------------------------------ - void onSelectionChanged(DynamicRibbonWidget* theWidget, - const std::string& selectionID, - const irr::core::stringw& selectionText, - const int playerID) - { - assert(m_magic_number == 0xCAFEC001); - - // Don't allow changing the selection after confirming it - if (m_parent->m_kart_widgets[playerID].isReady()) - { - // discard events sent when putting back to the right kart - if (selectionID == + // discard events sent when putting back to the right kart + if (selectionID == m_parent->m_kart_widgets[playerID].m_kartInternalName) return; - DynamicRibbonWidget* w = - m_parent->getWidget("karts"); - assert(w != NULL); + DynamicRibbonWidget* w = + m_parent->getWidget("karts"); + assert(w != NULL); - w->setSelection(m_parent->m_kart_widgets[playerID] - .m_kartInternalName, playerID, true); - return; - } + w->setSelection(m_parent->m_kart_widgets[playerID] + .m_kartInternalName, playerID, true); + return; + } - // Update the displayed model - ModelViewWidget* w3 = m_parent->m_kart_widgets[playerID].m_model_view; - assert( w3 != NULL ); + m_parent->updateKartWidgetModel(playerID, selectionID, selectionText); - if (selectionID == RANDOM_KART_ID) - { - // Random kart - scene::IMesh* model = - ItemManager::getItemModel(Item::ITEM_BONUS_BOX); - w3->clearModels(); - w3->addModel( model, Vec3(0.0f, -12.0f, 0.0f), - Vec3(35.0f, 35.0f, 35.0f) ); - w3->update(0); - m_parent->m_kart_widgets[playerID].m_kart_name - ->setText( _("Random Kart"), false ); - } - // selectionID contains the name of the kart, so check only for substr - else if (StringUtils::startsWith(selectionID, ID_LOCKED)) - { - w3->clearModels(); - w3->addModel(irr_driver->getAnimatedMesh( - file_manager->getDataDir() + "/models/chest.b3d" )->getMesh(20), - Vec3(0,0,0), Vec3(15.0f, 15.0f, 15.0f) ); - w3->update(0); - - if (m_parent->m_multiplayer) - { - m_parent->m_kart_widgets[playerID].m_kart_name - ->setText(_("Locked"), false ); - } - else - { - m_parent->m_kart_widgets[playerID].m_kart_name - ->setText(_("Locked : solve active challenges to gain " - "access to more!"), false ); - } - } - else - { - const KartProperties *kp = - kart_properties_manager->getKart(selectionID); - if (kp != NULL) - { - const KartModel &kart_model = kp->getMasterKartModel(); - - w3->clearModels(); - w3->addModel( kart_model.getModel(), Vec3(0,0,0), - Vec3(35.0f, 35.0f, 35.0f), - kart_model.getBaseFrame() ); - w3->addModel( kart_model.getWheelModel(0), - kart_model.getWheelGraphicsPosition(0) ); - w3->addModel( kart_model.getWheelModel(1), - kart_model.getWheelGraphicsPosition(1) ); - w3->addModel( kart_model.getWheelModel(2), - kart_model.getWheelGraphicsPosition(2) ); - w3->addModel( kart_model.getWheelModel(3), - kart_model.getWheelGraphicsPosition(3) ); - for(size_t i=0 ; i < kart_model.getSpeedWeightedObjectsCount() ; i++) - { - const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); - w3->addModel(obj.m_model, obj.m_position); - } - w3->update(0); - - m_parent->m_kart_widgets[playerID].m_kart_name - ->setText( selectionText.c_str(), false ); - } - else - { - fprintf(stderr, "[KartSelectionScreen] WARNING: could not " - "find a kart named '%s'\n", - selectionID.c_str()); - } - } - - m_parent->m_kart_widgets[playerID].setKartInternalName(selectionID); - m_parent->validateKartChoices(); - } // onSelectionChanged -}; // KartHoverListener + m_parent->m_kart_widgets[playerID].setKartInternalName(selectionID); + m_parent->validateKartChoices(); +} // onSelectionChanged #if 0 #pragma mark - @@ -989,7 +850,7 @@ public: // ============================================================================ -KartSelectionScreen::KartSelectionScreen() : Screen("karts.stkgui") +KartSelectionScreen::KartSelectionScreen(const char* filename) : Screen(filename) { m_removed_widget = NULL; m_multiplayer_message = NULL; @@ -997,6 +858,13 @@ KartSelectionScreen::KartSelectionScreen() : Screen("karts.stkgui") m_go_to_overworld_next = false; } // KartSelectionScreen +// ============================================================================ + +KartSelectionScreen* KartSelectionScreen::getRunningInstance() +{ + return m_instance_ptr; +} + // ---------------------------------------------------------------------------- void KartSelectionScreen::loadedFromFile() @@ -1061,6 +929,7 @@ void KartSelectionScreen::beforeAddingWidget() void KartSelectionScreen::init() { Screen::init(); + m_must_delete_on_back = false; RibbonWidget* tabs = getWidget("kartgroups"); assert( tabs != NULL ); @@ -1070,6 +939,8 @@ void KartSelectionScreen::init() Widget* placeholder = getWidget("playerskarts"); assert(placeholder != NULL); + // FIXME : The reserved id value is -1 when we switch from KSS to NKSS and vice-versa + g_dispatcher->setRootID(placeholder->m_reserved_id); g_root_id = placeholder->m_reserved_id; @@ -1170,6 +1041,9 @@ void KartSelectionScreen::tearDown() Screen::tearDown(); m_kart_widgets.clearAndDeleteAll(); + + if (m_must_delete_on_back) + GUIEngine::removeScreen(this->getName().c_str()); } // tearDown // ---------------------------------------------------------------------------- @@ -1194,20 +1068,20 @@ bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer) if (w == NULL) { std::cerr << "[KartSelectionScreen] playerJoin(): Called outside of " - "kart selection screen.\n"; + "kart selection screen.\n"; return false; } else if (device == NULL) { std::cerr << "[KartSelectionScreen] playerJoin(): Received null " - "device pointer\n"; + "device pointer\n"; return false; } if (StateManager::get()->activePlayerCount() >= MAX_PLAYER_COUNT) { std::cerr << "[KartSelectionScreen] Maximum number of players " - "reached\n"; + "reached\n"; sfx_manager->quickSound( "anvil" ); return false; } @@ -1250,7 +1124,7 @@ bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer) } const int new_player_id = - StateManager::get()->createActivePlayer( profileToUse, device ); + StateManager::get()->createActivePlayer( profileToUse, device, NULL ); StateManager::ActivePlayer* aplayer = StateManager::get()->getActivePlayer(new_player_id); @@ -1262,7 +1136,7 @@ bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer) // ---- Create player/kart widget PlayerKartWidget* newPlayerWidget = - new PlayerKartWidget(this, aplayer, kartsArea, m_kart_widgets.size(), + new PlayerKartWidget(this, aplayer, NULL, kartsArea, m_kart_widgets.size(), selected_kart_group); manualAddWidget(newPlayerWidget); @@ -1315,13 +1189,13 @@ bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer) w->setSelection(new_player_id, new_player_id, true); newPlayerWidget->m_player_ident_spinner - ->setFocusForPlayer(new_player_id); + ->setFocusForPlayer(new_player_id); } if (!m_multiplayer) { input_manager->getDeviceList()->setSinglePlayer( StateManager::get() - ->getActivePlayer(0)); + ->getActivePlayer(0)); } return true; @@ -1337,9 +1211,9 @@ bool KartSelectionScreen::playerQuit(StateManager::ActivePlayer* player) if (w == NULL) { std::cerr << "[KartSelectionScreen] ERROR: playerQuit() called " - "outside of kart selection screen, " + "outside of kart selection screen, " << "or the XML file for this screen was changed without " - "adapting the code accordingly\n"; + "adapting the code accordingly\n"; return false; } @@ -1372,7 +1246,7 @@ bool KartSelectionScreen::playerQuit(StateManager::ActivePlayer* player) if (playerID == -1) { std::cerr << "[KartSelectionScreen] WARNING: playerQuit cannot find " - "passed player\n"; + "passed player\n"; return false; } if(UserConfigParams::logGUI()) @@ -1515,14 +1389,14 @@ void KartSelectionScreen::playerConfirm(const int playerID) const bool player_ready = m_kart_widgets[n].isReady(); const bool ident_conflict = !m_kart_widgets[n].getAssociatedPlayer()->getProfile() - ->isGuestAccount() && + ->isGuestAccount() && m_kart_widgets[n].getAssociatedPlayer()->getProfile() == m_kart_widgets[playerID].getAssociatedPlayer()->getProfile(); const bool kart_conflict = sameKart(m_kart_widgets[n], - m_kart_widgets[playerID]); + m_kart_widgets[playerID]); if (player_ready && (ident_conflict || kart_conflict) && - !willNeedDuplicates) + !willNeedDuplicates) { if (UserConfigParams::logGUI()) printf("[KartSelectionScreen] You can't select this identity " @@ -1535,7 +1409,7 @@ void KartSelectionScreen::playerConfirm(const int playerID) // If two PlayerKart entries are associated to the same ActivePlayer, // something went wrong assert(m_kart_widgets[n].getAssociatedPlayer() != - m_kart_widgets[playerID].getAssociatedPlayer()); + m_kart_widgets[playerID].getAssociatedPlayer()); } // Mark this player as ready to start @@ -1569,6 +1443,83 @@ void KartSelectionScreen::playerConfirm(const int playerID) if (allPlayersReady && (!m_multiplayer || amount > 1)) allPlayersDone(); } // playerConfirm +// ---------------------------------------------------------------------------- + +void KartSelectionScreen::updateKartWidgetModel(uint8_t widget_id, + const std::string& selection, + const irr::core::stringw& selectionText) +{ + // Update the displayed model + ModelViewWidget* w3 = m_kart_widgets[widget_id].m_model_view; + assert( w3 != NULL ); + + if (selection == RANDOM_KART_ID) + { + // Random kart + scene::IMesh* model = + ItemManager::getItemModel(Item::ITEM_BONUS_BOX); + w3->clearModels(); + w3->addModel( model, Vec3(0.0f, -12.0f, 0.0f), + Vec3(35.0f, 35.0f, 35.0f) ); + w3->update(0); + m_kart_widgets[widget_id].m_kart_name + ->setText( _("Random Kart"), false ); + } + // selection contains the name of the kart, so check only for substr + else if (StringUtils::startsWith(selection, ID_LOCKED)) + { + w3->clearModels(); + w3->addModel(irr_driver->getAnimatedMesh( + file_manager->getDataDir() + "/models/chest.b3d" )->getMesh(20), + Vec3(0,0,0), Vec3(15.0f, 15.0f, 15.0f) ); + w3->update(0); + + if (m_multiplayer) + { + m_kart_widgets[widget_id].m_kart_name + ->setText(_("Locked"), false ); + } + else + { + m_kart_widgets[widget_id].m_kart_name + ->setText(_("Locked : solve active challenges to gain " + "access to more!"), false ); + } + } + else + { + const KartProperties *kp = + kart_properties_manager->getKart(selection); + if (kp != NULL) + { + const KartModel &kart_model = kp->getMasterKartModel(); + + w3->clearModels(); + w3->addModel( kart_model.getModel(), Vec3(0,0,0), + Vec3(35.0f, 35.0f, 35.0f), + kart_model.getBaseFrame() ); + w3->addModel( kart_model.getWheelModel(0), + kart_model.getWheelGraphicsPosition(0) ); + w3->addModel( kart_model.getWheelModel(1), + kart_model.getWheelGraphicsPosition(1) ); + w3->addModel( kart_model.getWheelModel(2), + kart_model.getWheelGraphicsPosition(2) ); + w3->addModel( kart_model.getWheelModel(3), + kart_model.getWheelGraphicsPosition(3) ); + w3->update(0); + + m_kart_widgets[widget_id].m_kart_name + ->setText( selectionText.c_str(), false ); + } + else + { + fprintf(stderr, "[KartSelectionScreen] WARNING: could not " + "find a kart named '%s'\n", + selection.c_str()); + } + } +} + // ---------------------------------------------------------------------------- /** * Callback handling events from the kart selection menu @@ -1617,7 +1568,7 @@ void KartSelectionScreen::eventCallback(Widget* widget, // the tab switch if (UserConfigParams::logGUI()) std::cout << "[KartSelectionScreen] Player " << n - << " lost their selection when switching tabs!!!\n"; + << " lost their selection when switching tabs!!!\n"; // Select a random kart in this case const int count = w->getItems().size(); @@ -1635,13 +1586,13 @@ void KartSelectionScreen::eventCallback(Widget* widget, n != PLAYER_ID_GAME_MASTER ); if (!success) std::cerr << "[KartSelectionScreen] WARNING: " - "setting kart of player " << n + "setting kart of player " << n << " failed :(\n"; } else { std::cerr << "[KartSelectionScreen] WARNING : 0 items " - "in the ribbon\n"; + "in the ribbon\n"; } } } @@ -1654,7 +1605,7 @@ void KartSelectionScreen::eventCallback(Widget* widget, else if (name == "back") { m_go_to_overworld_next = false; // valid once - + m_must_delete_on_back = true; if (m_from_overworld) { m_from_overworld = false; // valid once @@ -1693,7 +1644,7 @@ void KartSelectionScreen::setMultiplayer(bool multiplayer) bool KartSelectionScreen::onEscapePressed() { m_go_to_overworld_next = false; // valid once - + m_must_delete_on_back = true; // delete the screen if (m_from_overworld) { m_from_overworld = false; // valid once @@ -1739,16 +1690,16 @@ void KartSelectionScreen::allPlayersDone() for (int n=0; ngetName().c_str()).c_str() - << " on " << players[n].getDevice()->m_name << std::endl; + << core::stringc( + players[n].getConstProfile()->getName().c_str()).c_str() + << " on " << players[n].getDevice()->m_name << std::endl; } } for (int n=0; ngetActivePlayer(n)->getProfile() - ->incrementUseFrequency(); + ->incrementUseFrequency(); } // ---- Give player info to race manager race_manager->setNumLocalPlayers( players.size() ); @@ -1789,8 +1740,8 @@ void KartSelectionScreen::allPlayersDone() { randomID = random.get(item_count); if (items[randomID].m_code_name != ID_DONT_USE && - !StringUtils::startsWith(items[randomID].m_code_name, - ID_LOCKED)) + !StringUtils::startsWith(items[randomID].m_code_name, + ID_LOCKED)) { selected_kart = items[randomID].m_code_name; done = true; @@ -1798,7 +1749,8 @@ void KartSelectionScreen::allPlayersDone() items[randomID].m_code_name = ID_DONT_USE; count++; if (count > 100) return; - } while (!done); + } + while (!done); } else { @@ -1824,7 +1776,7 @@ void KartSelectionScreen::allPlayersDone() if (!m_multiplayer) { input_manager->getDeviceList() - ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); + ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); } else { @@ -1865,8 +1817,8 @@ bool KartSelectionScreen::validateIdentChoices() if (m_multiplayer) { assert( m_kart_widgets[n].getAssociatedPlayer()->getProfile() == - UserConfigParams::m_all_players.get(m_kart_widgets[n] - .m_player_ident_spinner->getValue()) ); + UserConfigParams::m_all_players.get(m_kart_widgets[n] + .m_player_ident_spinner->getValue()) ); } } } @@ -1877,7 +1829,7 @@ bool KartSelectionScreen::validateIdentChoices() // skip players that took a guest account, they can be many on the // same identity in this case if (m_kart_widgets[n].getAssociatedPlayer()->getProfile() - ->isGuestAccount()) + ->isGuestAccount()) { continue; } @@ -1888,24 +1840,24 @@ bool KartSelectionScreen::validateIdentChoices() // check if 2 players took the same name if (m_kart_widgets[n].getAssociatedPlayer()->getProfile() == - m_kart_widgets[m].getAssociatedPlayer()->getProfile()) + m_kart_widgets[m].getAssociatedPlayer()->getProfile()) { // two players took the same name. check if one is ready if (!m_kart_widgets[n].isReady() && - m_kart_widgets[m].isReady()) + m_kart_widgets[m].isReady()) { // player m is ready, so player n should not choose // this name m_kart_widgets[n].m_player_ident_spinner - ->markAsIncorrect(); + ->markAsIncorrect(); } else if (m_kart_widgets[n].isReady() && - !m_kart_widgets[m].isReady()) + !m_kart_widgets[m].isReady()) { // player n is ready, so player m should not // choose this name m_kart_widgets[m].m_player_ident_spinner - ->markAsIncorrect(); + ->markAsIncorrect(); } else if (m_kart_widgets[n].isReady() && m_kart_widgets[m].isReady()) @@ -1965,7 +1917,7 @@ bool KartSelectionScreen::validateKartChoices() // two players took the same kart. check if one is ready if (!m_kart_widgets[n].isReady() && - m_kart_widgets[m].isReady()) + m_kart_widgets[m].isReady()) { if (UserConfigParams::logGUI()) std::cout << " --> Setting red badge on player " @@ -1976,7 +1928,7 @@ bool KartSelectionScreen::validateKartChoices() m_kart_widgets[n].m_model_view->setBadge(BAD_BADGE); } else if (m_kart_widgets[n].isReady() && - !m_kart_widgets[m].isReady()) + !m_kart_widgets[m].isReady()) { if (UserConfigParams::logGUI()) std::cout << " --> Setting red badge on player " @@ -2040,7 +1992,7 @@ void KartSelectionScreen::setKartsFromCurrentGroup() // selected kart group is removed. In this case, select the // 'standard' group if (selected_kart_group != ALL_KART_GROUPS_ID && - !kart_properties_manager->getKartsInGroup(selected_kart_group).size()) + !kart_properties_manager->getKartsInGroup(selected_kart_group).size()) { selected_kart_group = DEFAULT_GROUP_NAME; } @@ -2065,7 +2017,7 @@ void KartSelectionScreen::setKartsFromCurrentGroup() "to more!"), ID_LOCKED+prop->getIdent(), prop->getAbsoluteIconFile(), LOCKED_BADGE, - IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); } else { @@ -2101,7 +2053,7 @@ void KartSelectionScreen::setKartsFromCurrentGroup() else { w->addItem(translations->fribidize(prop->getName()), - prop->getIdent(), + prop->getIdent(), icon_path, 0, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); usableKartCount++; @@ -2154,7 +2106,7 @@ EventPropagation FocusDispatcher::focused(const int playerID) // ")" << std::endl; m_parent->m_kart_widgets[n].m_player_ident_spinner - ->setFocusForPlayer(playerID); + ->setFocusForPlayer(playerID); return GUIEngine::EVENT_BLOCK; diff --git a/src/states_screens/kart_selection.hpp b/src/states_screens/kart_selection.hpp index fc6f1ee00..557b612fd 100644 --- a/src/states_screens/kart_selection.hpp +++ b/src/states_screens/kart_selection.hpp @@ -1,6 +1,6 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 +// +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,14 +16,27 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef KART_SELECTION_INCLUDED +#define KART_SELECTION_INCLUDED + #include #include "guiengine/screen.hpp" +#include "guiengine/widgets/dynamic_ribbon_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/model_view_widget.hpp" +#include "guiengine/widgets/spinner_widget.hpp" #include "states_screens/state_manager.hpp" +#include namespace GUIEngine { class Widget; class BubbleWidget; + enum EventPropagation; +} +namespace Online +{ + class User; } class InputDevice; class PlayerKartWidget; @@ -33,13 +46,12 @@ class KartHoverListener; * \brief screen where players can choose their kart * \ingroup states_screens */ -class KartSelectionScreen : public GUIEngine::Screen, - public GUIEngine::ScreenSingleton +class KartSelectionScreen : public GUIEngine::Screen { friend class KartHoverListener; friend class PlayerNameSpinner; friend class FocusDispatcher; - +protected: /** Contains the custom widget shown for every player. (ref only since * we're adding them to a Screen, and the Screen will take ownership * of these widgets) @@ -56,8 +68,9 @@ class KartSelectionScreen : public GUIEngine::Screen, bool m_go_to_overworld_next; + bool m_must_delete_on_back; //!< To delete the screen if back is pressed - KartSelectionScreen(); + KartSelectionScreen(const char* filename); /** Stores whether any player confirmed their choice; then, some things * are "frozen", for instance the selected kart group tab @@ -90,9 +103,17 @@ class KartSelectionScreen : public GUIEngine::Screen, /** Fill the ribbon with the karts from the currently selected group */ void setKartsFromCurrentGroup(); - void playerConfirm(const int playerID); + virtual void playerConfirm(const int playerID); + /** updates model of a kart widget, to have the good selection when the user validates */ + void updateKartWidgetModel(uint8_t widget_id, + const std::string& selection, + const irr::core::stringw& selectionText); + /** Stores a pointer to the current selection screen */ + static KartSelectionScreen* m_instance_ptr; public: + /** Returns the current instance */ + static KartSelectionScreen* getRunningInstance(); /** \brief implement callback from parent class GUIEngine::Screen */ virtual void loadedFromFile() OVERRIDE; @@ -139,3 +160,209 @@ public: virtual bool onEscapePressed() OVERRIDE; }; // KartSelectionScreen + +//!---------------------------------------------------------------------------- +//! FocusDispatcher : +/** Currently, navigation for multiple players at the same time is implemented + in a somewhat clunky way. An invisible "dispatcher" widget is added above + kart icons. When a player moves up, he focuses the dispatcher, which in + turn moves the selection to the appropriate spinner. "tabbing roots" are + used to make navigation back down possible. (FIXME: maybe find a cleaner + way?) */ +class FocusDispatcher : public GUIEngine::Widget +{ + KartSelectionScreen* m_parent; + int m_reserved_id; + + bool m_is_initialised; + +public: + + LEAK_CHECK() + + // ------------------------------------------------------------------------ + FocusDispatcher(KartSelectionScreen* parent); + // ------------------------------------------------------------------------ + void setRootID(const int reservedID); + + // ------------------------------------------------------------------------ + virtual void add(); + + // ------------------------------------------------------------------------ + + virtual GUIEngine::EventPropagation focused(const int playerID); +}; // FocusDispatcher + +//!---------------------------------------------------------------------------- +//! PlayerNameSpinner : +/** A small extension to the spinner widget to add features like player ID + * management or badging */ +class PlayerNameSpinner : public GUIEngine::SpinnerWidget +{ + int m_playerID; + bool m_incorrect; + irr::gui::IGUIImage* m_red_mark_widget; + KartSelectionScreen* m_parent; + + //virtual EventPropagation focused(const int m_playerID) ; + +public: + PlayerNameSpinner(KartSelectionScreen* parent, const int playerID); + // ------------------------------------------------------------------------ + void setID(const int m_playerID); + // ------------------------------------------------------------------------ + /** Add a red mark on the spinner to mean "invalid choice" */ + void markAsIncorrect(); + + // ------------------------------------------------------------------------ + /** Remove any red mark set with 'markAsIncorrect' */ + void markAsCorrect(); +}; + +/** A widget representing the kart selection for a player (i.e. the player's + * number, name, the kart view, the kart's name) */ +class PlayerKartWidget : public GUIEngine::Widget, + public GUIEngine::SpinnerWidget::ISpinnerConfirmListener +{ + /** Whether this player confirmed their selection */ + bool m_ready; + + /** widget coordinates */ + int player_id_x, player_id_y, player_id_w, player_id_h; + int player_name_x, player_name_y, player_name_w, player_name_h; + int model_x, model_y, model_w, model_h; + int kart_name_x, kart_name_y, kart_name_w, kart_name_h; + + /** A reserved ID for this widget if any, -1 otherwise. (If no ID is + * reserved, widget will not be in the regular tabbing order */ + int m_irrlicht_widget_ID; + + /** For animation purposes (see method 'move') */ + int target_x, target_y, target_w, target_h; + float x_speed, y_speed, w_speed, h_speed; + + /** Object representing this player */ + StateManager::ActivePlayer* m_associatedPlayer; // local info + int m_playerID; + Online::Profile* m_associated_user; // network info + + /** Internal name of the spinner; useful to interpret spinner events, + * which contain the name of the activated object */ + std::string spinnerID; + +#ifdef DEBUG + long m_magic_number; +#endif + +public: + + LEAK_CHECK() + + /** Sub-widgets created by this widget */ + //LabelWidget* m_player_ID_label; + PlayerNameSpinner* m_player_ident_spinner; + GUIEngine::ModelViewWidget* m_model_view; + GUIEngine::LabelWidget* m_kart_name; + + KartSelectionScreen* m_parent_screen; + + irr::gui::IGUIStaticText* m_ready_text; + + //LabelWidget *getPlayerIDLabel() {return m_player_ID_label;} + core::stringw deviceName; + std::string m_kartInternalName; + + bool m_not_updated_yet; + + PlayerKartWidget(KartSelectionScreen* parent, + StateManager::ActivePlayer* associatedPlayer, + Online::Profile* associatedUser, + core::recti area, const int playerID, + std::string kartGroup, + const int irrlichtWidgetID=-1); + // ------------------------------------------------------------------------ + + ~PlayerKartWidget(); + + // ------------------------------------------------------------------------ + /** Called when players are renumbered (changes the player ID) */ + void setPlayerID(const int newPlayerID); + + // ------------------------------------------------------------------------ + /** Returns the ID of this player */ + int getPlayerID() const; + + // ------------------------------------------------------------------------ + /** Add the widgets to the current screen */ + virtual void add(); + + // ------------------------------------------------------------------------ + /** Get the associated ActivePlayer object*/ + StateManager::ActivePlayer* getAssociatedPlayer(); + + // ------------------------------------------------------------------------ + /** Starts a 'move/resize' animation, by simply passing destination coords. + * The animation will then occur on each call to 'onUpdate'. */ + void move(const int x, const int y, const int w, const int h); + + // ------------------------------------------------------------------------ + /** Call when player confirmed his identity and kart */ + void markAsReady(); + + // ------------------------------------------------------------------------ + /** \return Whether this player confirmed his kart and indent selection */ + bool isReady(); + + // ------------------------------------------------------------------------- + /** Updates the animation (moving/shrinking/etc.) */ + void onUpdate(float delta); + + // ------------------------------------------------------------------------- + /** Event callback */ + virtual GUIEngine::EventPropagation transmitEvent( + GUIEngine::Widget* w, + const std::string& originator, + const int m_playerID); + + // ------------------------------------------------------------------------- + /** Sets the size of the widget as a whole, and placed children widgets + * inside itself */ + void setSize(const int x, const int y, const int w, const int h); + + // ------------------------------------------------------------------------- + + /** Sets which kart was selected for this player */ + void setKartInternalName(const std::string& whichKart); + + // ------------------------------------------------------------------------- + + const std::string& getKartInternalName() const; + + // ------------------------------------------------------------------------- + + /** \brief Event callback from ISpinnerConfirmListener */ + virtual GUIEngine::EventPropagation onSpinnerConfirmed(); +}; // PlayerKartWidget + +//!---------------------------------------------------------------------------- +//! KartHoverListener : + +class KartHoverListener : public GUIEngine::DynamicRibbonHoverListener +{ + KartSelectionScreen* m_parent; +public: + unsigned int m_magic_number; + + KartHoverListener(KartSelectionScreen* parent); + + // ------------------------------------------------------------------------ + virtual ~KartHoverListener(); + + // ------------------------------------------------------------------------ + void onSelectionChanged(GUIEngine::DynamicRibbonWidget* theWidget, + const std::string& selectionID, + const irr::core::stringw& selectionText, + const int playerID); +}; // KartHoverListener + +#endif diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 9169e1000..33151a2a6 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -37,11 +37,12 @@ #include "modes/cutscene_world.hpp" #include "modes/overworld.hpp" #include "modes/demo_world.hpp" -#include "network/network_manager.hpp" +#include "states_screens/online_screen.hpp" #include "states_screens/addons_screen.hpp" #include "states_screens/credits.hpp" #include "states_screens/help_screen_1.hpp" -#include "states_screens/kart_selection.hpp" +#include "states_screens/offline_kart_selection.hpp" +#include "states_screens/network_kart_selection.hpp" // FIXME : remove when not testing #include "states_screens/options_screen_video.hpp" #include "states_screens/state_manager.hpp" @@ -271,14 +272,14 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, #endif if (selection == "new") { - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); //FIXME : that was for tests s->setMultiplayer(false); s->setFromOverworld(false); StateManager::get()->pushScreen( s ); } else if (selection == "multiplayer") { - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); s->setMultiplayer(true); s->setFromOverworld(false); StateManager::get()->pushScreen( s ); @@ -315,7 +316,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, // Create player and associate player with keyboard StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + device, NULL); if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL) { @@ -332,7 +333,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } else if (selection == "story") @@ -359,7 +360,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, const std::string default_kart = UserConfigParams::m_default_kart; if (slot->isLocked(default_kart)) { - KartSelectionScreen *next = KartSelectionScreen::getInstance(); + KartSelectionScreen *next = OfflineKartSelectionScreen::getInstance(); next->setGoToOverworldNext(); next->setMultiplayer(false); StateManager::get()->resetAndGoToScreen(next); @@ -368,6 +369,10 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, OverWorld::enterOverWorld(); } } + else if (selection == "online") + { + StateManager::get()->pushScreen(OnlineScreen::getInstance()); + } else if (selection == "addons") { StateManager::get()->pushScreen(AddonsScreen::getInstance()); @@ -403,4 +408,4 @@ void MainMenuScreen::onDisabledItemClicked(const std::string& item) new MessageDialog( _("Please wait while the add-ons are loading")); } } -} // onDisabledItemClicked \ No newline at end of file +} // onDisabledItemClicked diff --git a/src/states_screens/main_menu_screen.hpp b/src/states_screens/main_menu_screen.hpp index 696c7a6bc..5683c7923 100644 --- a/src/states_screens/main_menu_screen.hpp +++ b/src/states_screens/main_menu_screen.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp new file mode 100644 index 000000000..1c419eb7a --- /dev/null +++ b/src/states_screens/network_kart_selection.cpp @@ -0,0 +1,184 @@ +#include "states_screens/network_kart_selection.hpp" + +#include "audio/sfx_manager.hpp" +#include "challenges/unlock_manager.hpp" +#include "items/item_manager.hpp" +#include "karts/kart_properties.hpp" +#include "karts/kart_properties_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" +#include "network/network_manager.hpp" +#include "online/current_user.hpp" +#include "states_screens/state_manager.hpp" + +static const char RANDOM_KART_ID[] = "randomkart"; +static const char ID_LOCKED[] = "locked/"; + +using namespace GUIEngine; + +DEFINE_SCREEN_SINGLETON( NetworkKartSelectionScreen ); + +NetworkKartSelectionScreen::NetworkKartSelectionScreen() : KartSelectionScreen("karts_online.stkgui") +{ + KartSelectionScreen::m_instance_ptr = this; +} + +NetworkKartSelectionScreen::~NetworkKartSelectionScreen() +{ +} + +void NetworkKartSelectionScreen::init() +{ + m_multiplayer = false; + KartSelectionScreen::init(); + + RibbonWidget* tabs = getWidget("kartgroups"); + assert( tabs != NULL ); + tabs->select( "standard", PLAYER_ID_GAME_MASTER); // select standard kart group + tabs->setDeactivated(); + tabs->setVisible(false); + + // change the back button image (because it makes the game quit) + IconButtonWidget* back_button = getWidget("back"); + back_button->setImage("gui/main_quit.png"); + + m_multiplayer = false; + + // add a widget for each player except self (already exists): + GameSetup* setup = NetworkManager::getInstance()->getGameSetup(); + if (!setup) + { + Log::error("NetworkKartSelectionScreen", "No network game setup registered."); + return; + } + std::vector players = setup->getPlayers(); + + Log::info("NKSS", "There are %d players", players.size()); + // ---- Get available area for karts + // make a copy of the area, ands move it to be outside the screen + Widget* kartsAreaWidget = getWidget("playerskarts"); + // start at the rightmost of the screen + const int shift = irr_driver->getFrameSize().Width; + core::recti kartsArea(kartsAreaWidget->m_x + shift, + kartsAreaWidget->m_y, + kartsAreaWidget->m_x + shift + kartsAreaWidget->m_w, + kartsAreaWidget->m_y + kartsAreaWidget->m_h); + + for (unsigned int i = 0; i < players.size(); i++) + { + if (players[i]->user_profile == Online::CurrentUser::get()->getProfile()) + { + m_id_mapping.insert(m_id_mapping.begin(),players[i]->race_id); //!< first kart widget always me + Log::info("NKSS", "Insert %d at pos 0", players[i]->race_id); + continue; // it is me, don't add again + } + + Log::info("NKSS", "Adding %d at pos %d", players[i]->race_id, i); + m_id_mapping.push_back(players[i]->race_id); + + StateManager::ActivePlayer* aplayer = NULL; // player is remote + + std::string selected_kart_group = "standard"; // standard group + + PlayerKartWidget* newPlayerWidget = + new PlayerKartWidget(this, aplayer, players[i]->user_profile, kartsArea, m_kart_widgets.size(), + selected_kart_group); + + manualAddWidget(newPlayerWidget); + m_kart_widgets.push_back(newPlayerWidget); + + newPlayerWidget->add(); + } + + const int amount = m_kart_widgets.size(); + Widget* fullarea = getWidget("playerskarts"); + + const int splitWidth = fullarea->m_w / amount; + + for (int n=0; nm_x + splitWidth*n, + fullarea->m_y, splitWidth, fullarea->m_h); + } + +} + +void NetworkKartSelectionScreen::playerConfirm(const int playerID) +{ + DynamicRibbonWidget* w = getWidget("karts"); + assert(w != NULL); + const std::string selection = w->getSelectionIDString(playerID); + if (StringUtils::startsWith(selection, ID_LOCKED)) + { + unlock_manager->playLockSound(); + return; + } + + if (playerID == PLAYER_ID_GAME_MASTER) + { + UserConfigParams::m_default_kart = selection; + } + + if (m_kart_widgets[playerID].getKartInternalName().size() == 0) + { + sfx_manager->quickSound( "anvil" ); + return; + } + if(playerID == PLAYER_ID_GAME_MASTER) // self + { + ClientLobbyRoomProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + protocol->requestKartSelection(selection); + } +} + +void NetworkKartSelectionScreen::playerSelected(uint8_t race_id, std::string kart_name) +{ + uint8_t widget_id = -1; + for (unsigned int i = 0; i < m_id_mapping.size(); i++) + { + Log::info("NKSS", "Checking race id %d : mapped of %d is %d", race_id, i, m_id_mapping[i]); + if (m_id_mapping[i] == race_id) + widget_id = i; + } + + assert(widget_id>=0 && widget_id < m_kart_widgets.size()); + + KartSelectionScreen::updateKartWidgetModel(widget_id, kart_name, irr::core::stringw(kart_name.c_str())); + m_kart_widgets[widget_id].setKartInternalName(kart_name); + m_kart_widgets[widget_id].markAsReady(); // mark player ready +} + + +/** + * Callback handling events from the kart selection menu + */ +void NetworkKartSelectionScreen::eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) +{ + if (name == "karts") + { + KartSelectionScreen::eventCallback(widget, name, playerID); + } + else if (name == "back") + { + KartSelectionScreen::eventCallback(widget, name, playerID); + } + else // name != karts + { + KartSelectionScreen::eventCallback(widget, name, playerID); + } +} // eventCallback + + +bool NetworkKartSelectionScreen::onEscapePressed() +{ + // then remove the lobby screen (you left the server) + StateManager::get()->popMenu(); + // notify the server that we left + ClientLobbyRoomProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + if (protocol) + protocol->leave(); + return true; // remove the screen +} diff --git a/src/states_screens/network_kart_selection.hpp b/src/states_screens/network_kart_selection.hpp new file mode 100644 index 000000000..524fb433c --- /dev/null +++ b/src/states_screens/network_kart_selection.hpp @@ -0,0 +1,29 @@ +#ifndef NETWORK_KART_SELECTION_HPP +#define NETWORK_KART_SELECTION_HPP + +#include "states_screens/kart_selection.hpp" +#include "guiengine/screen.hpp" + +class NetworkKartSelectionScreen : public KartSelectionScreen, public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; +protected: + //!< map the id of the kart widgets to race ids + std::vector m_id_mapping; + + NetworkKartSelectionScreen(); + virtual ~NetworkKartSelectionScreen(); + + virtual void playerConfirm(const int playerID); + void considerKartHovered(uint8_t widget_id, std::string selection); +public: + virtual void init() OVERRIDE; + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + virtual bool onEscapePressed() OVERRIDE; + + virtual void playerSelected(uint8_t race_id, std::string kart_name); +}; + +#endif // NETWORK_KART_SELECTION_HPP diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp new file mode 100644 index 000000000..f2cc525d1 --- /dev/null +++ b/src/states_screens/networking_lobby.cpp @@ -0,0 +1,152 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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. + +#define DEBUG_MENU_ITEM 0 + +#include "states_screens/networking_lobby.hpp" + +#include +#include + +#include "challenges/game_slot.hpp" +#include "challenges/unlock_manager.hpp" +#include "graphics/irr_driver.hpp" +#include "guiengine/scalable_font.hpp" +#include "input/device_manager.hpp" +#include "input/input_manager.hpp" +#include "io/file_manager.hpp" +#include "main_loop.hpp" +#include "states_screens/online_screen.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/dialogs/message_dialog.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" +#include "modes/demo_world.hpp" +#include "utils/translation.hpp" +#include "online/servers_manager.hpp" + +using namespace Online; +using namespace GUIEngine; + +DEFINE_SCREEN_SINGLETON( NetworkingLobby ); + +// ---------------------------------------------------------------------------- + +NetworkingLobby::NetworkingLobby() : Screen("online/lobby.stkgui") +{ + m_server = NULL; +} // NetworkingLobby + +// ---------------------------------------------------------------------------- + +void NetworkingLobby::loadedFromFile() +{ + m_back_widget = getWidget("back"); + assert(m_back_widget != NULL); + + m_server_name_widget = getWidget("server_name"); + assert(m_server_name_widget != NULL); + + m_online_status_widget = getWidget("online_status"); + assert(m_online_status_widget != NULL); + + m_bottom_menu_widget = getWidget("menu_bottomrow"); + assert(m_bottom_menu_widget != NULL); + + m_exit_widget = (IconButtonWidget *) m_bottom_menu_widget->findWidgetNamed("exit"); + assert(m_exit_widget != NULL); + +} // loadedFromFile + +// ---------------------------------------------------------------------------- +void NetworkingLobby::beforeAddingWidget() +{ + +} // beforeAddingWidget + + + +// ---------------------------------------------------------------------------- +void NetworkingLobby::init() +{ + Screen::init(); + setInitialFocus(); + DemoWorld::resetIdleTime(); //FIXME : what's this? + m_server = ServersManager::get()->getJoinedServer(); + m_server_name_widget->setText(m_server->getName(),false); + +} // init + +// ---------------------------------------------------------------------------- +void NetworkingLobby::onUpdate(float delta, irr::video::IVideoDriver* driver) +{ +} // onUpdate + +// ---------------------------------------------------------------------------- + +void NetworkingLobby::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + if (name == m_back_widget->m_properties[PROP_ID]) + { + StateManager::get()->escapePressed(); + return; + } + + RibbonWidget* ribbon = dynamic_cast(widget); + if (ribbon == NULL) return; + std::string selection = ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER); + + if (selection == m_exit_widget->m_properties[PROP_ID]) + { + StateManager::get()->escapePressed(); + } +} // eventCallback + +// ---------------------------------------------------------------------------- + +void NetworkingLobby::tearDown() +{ +} // tearDown + +// ---------------------------------------------------------------------------- + +bool NetworkingLobby::onEscapePressed() +{ + // notify the server that we left + ClientLobbyRoomProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + if (protocol) + protocol->leave(); + return true; // close the screen +} + +// ---------------------------------------------------------------------------- +void NetworkingLobby::onDisabledItemClicked(const std::string& item) +{ + +} // onDisabledItemClicked + +// ---------------------------------------------------------------------------- +void NetworkingLobby::setInitialFocus() +{ +} // setInitialFocus + +// ---------------------------------------------------------------------------- +void NetworkingLobby::onDialogClose() +{ + setInitialFocus(); +} // onDialogClose() diff --git a/src/states_screens/networking_lobby.hpp b/src/states_screens/networking_lobby.hpp new file mode 100644 index 000000000..96f89b37b --- /dev/null +++ b/src/states_screens/networking_lobby.hpp @@ -0,0 +1,85 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_NETWORKING_LOBBY_HPP +#define HEADER_NETWORKING_LOBBY_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "online/server.hpp" + +namespace GUIEngine { class Widget; class ListWidget; } + +/** + * \brief Handles the main menu + * \ingroup states_screens + */ +class NetworkingLobby : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton +{ +private: + friend class GUIEngine::ScreenSingleton; + + Online::Server * m_server; + + NetworkingLobby(); + + GUIEngine::IconButtonWidget * m_back_widget; + + GUIEngine::LabelWidget * m_server_name_widget; + + GUIEngine::LabelWidget * m_online_status_widget; + + GUIEngine::RibbonWidget * m_bottom_menu_widget; + GUIEngine::IconButtonWidget * m_exit_widget; + + /** \brief Sets which widget has to be focused. Depends on the user state. */ + void setInitialFocus(); + +public: + + virtual void onUpdate(float delta, irr::video::IVideoDriver* driver) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void tearDown() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual bool onEscapePressed() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void onDisabledItemClicked(const std::string& item) OVERRIDE; + + /** \brief Implements the callback when a dialog gets closed. */ + virtual void onDialogClose() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/offline_kart_selection.cpp b/src/states_screens/offline_kart_selection.cpp new file mode 100644 index 000000000..296678360 --- /dev/null +++ b/src/states_screens/offline_kart_selection.cpp @@ -0,0 +1,15 @@ +#include "states_screens/offline_kart_selection.hpp" + +DEFINE_SCREEN_SINGLETON( OfflineKartSelectionScreen ); + +OfflineKartSelectionScreen::OfflineKartSelectionScreen() : KartSelectionScreen("karts.stkgui") +{ + KartSelectionScreen::m_instance_ptr = this; +} + +OfflineKartSelectionScreen::~OfflineKartSelectionScreen() +{ + //dtor +} + +bool OfflineKartSelectionScreen::isRunning() { return singleton!=NULL; } diff --git a/src/states_screens/offline_kart_selection.hpp b/src/states_screens/offline_kart_selection.hpp new file mode 100644 index 000000000..5cfe747fb --- /dev/null +++ b/src/states_screens/offline_kart_selection.hpp @@ -0,0 +1,20 @@ +#ifndef OFFLINE_KART_SELECTION_HPP +#define OFFLINE_KART_SELECTION_HPP + +#include "states_screens/kart_selection.hpp" +#include "guiengine/screen.hpp" + +class OfflineKartSelectionScreen : public KartSelectionScreen, public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; + protected: + OfflineKartSelectionScreen(); + virtual ~OfflineKartSelectionScreen(); + + public: + static bool isRunning(); + + // we do not override anything, this class is just there to have a singleton +}; + +#endif // OFFLINE_KART_SELECTION_HPP diff --git a/src/states_screens/online_profile_achievements.cpp b/src/states_screens/online_profile_achievements.cpp new file mode 100644 index 000000000..984800d06 --- /dev/null +++ b/src/states_screens/online_profile_achievements.cpp @@ -0,0 +1,134 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/online_profile_achievements.hpp" + +#include "achievements/achievements_manager.hpp" +#include "guiengine/engine.hpp" +#include "guiengine/scalable_font.hpp" +#include "guiengine/screen.hpp" +#include "guiengine/widget.hpp" +#include "states_screens/dialogs/message_dialog.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/dialogs/user_info_dialog.hpp" +#include "utils/translation.hpp" +#include "online/messages.hpp" + +#include + +#include +#include + +using namespace GUIEngine; +using namespace irr::core; +using namespace irr::gui; +using namespace Online; + +DEFINE_SCREEN_SINGLETON( OnlineProfileAchievements ); + +// ----------------------------------------------------------------------------- + +OnlineProfileAchievements::OnlineProfileAchievements() : OnlineProfileBase("online/profile_achievements.stkgui") +{ + m_selected_achievement_index = -1; +} // OnlineProfileAchievements + +// ----------------------------------------------------------------------------- + +void OnlineProfileAchievements::loadedFromFile() +{ + OnlineProfileBase::loadedFromFile(); + m_achievements_list_widget = getWidget("achievements_list"); + assert(m_achievements_list_widget != NULL); + +} // loadedFromFile + +// ---------------------------------------------------------------------------- + +void OnlineProfileAchievements::beforeAddingWidget() +{ + OnlineProfileBase::beforeAddingWidget(); + m_achievements_list_widget->clearColumns(); + m_achievements_list_widget->addColumn( _("Name"), 2 ); + if(m_visiting_profile->isCurrentUser()) + { + m_achievements_list_widget->addColumn( _("Progress"), 1 ); + } +} + +// ----------------------------------------------------------------------------- + +void OnlineProfileAchievements::init() +{ + OnlineProfileBase::init(); + m_profile_tabs->select( m_achievements_tab->m_properties[PROP_ID], PLAYER_ID_GAME_MASTER ); + assert(m_visiting_profile != NULL); + if(m_visiting_profile->isCurrentUser()) + { + m_waiting_for_achievements = false; + m_achievements_list_widget->clear(); + const std::map & all_achievements = AchievementsManager::get()->getActive()->getAllAchievements(); + std::map::const_iterator it; + for ( it = all_achievements.begin(); it != all_achievements.end(); ++it ) { + PtrVector * row = new PtrVector; + row->push_back(new GUIEngine::ListWidget::ListCell(it->second->getInfo()->getTitle(),-1,2)); + row->push_back(new GUIEngine::ListWidget::ListCell(it->second->getProgressAsString(),-1,1, true)); + m_achievements_list_widget->addItem(StringUtils::toString(it->second->getInfo()->getID()), row); + } + } + else + { + m_waiting_for_achievements = true; + m_visiting_profile->fetchAchievements(); + m_achievements_list_widget->clear(); + m_achievements_list_widget->addItem("loading", Messages::fetchingAchievements()); + } +} // init +// ----------------------------------------------------------------------------- + +void OnlineProfileAchievements::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + OnlineProfileBase::eventCallback( widget, name, playerID); + if (name == m_achievements_list_widget->m_properties[GUIEngine::PROP_ID]) + { + m_selected_achievement_index = m_achievements_list_widget->getSelectionID(); + + new MessageDialog(AchievementsManager::get()->getAchievementInfo(atoi(m_achievements_list_widget->getSelectionInternalName().c_str()))->getDescription()); + } +} // eventCallback + +// ---------------------------------------------------------------------------- +void OnlineProfileAchievements::onUpdate(float delta, irr::video::IVideoDriver* driver) +{ + if(m_waiting_for_achievements) + { + if(m_visiting_profile->isReady()) + { + m_achievements_list_widget->clear(); + for(unsigned int i = 0; i < m_visiting_profile->getAchievements().size(); i++) + { + AchievementInfo * info = AchievementsManager::get()->getAchievementInfo(m_visiting_profile->getAchievements()[i]); + m_achievements_list_widget->addItem(StringUtils::toString(info->getID()), info->getTitle()); + } + m_waiting_for_achievements = false; + } + else + { + m_achievements_list_widget->renameItem("loading", Messages::fetchingFriends()); + } + } +} diff --git a/src/states_screens/online_profile_achievements.hpp b/src/states_screens/online_profile_achievements.hpp new file mode 100644 index 000000000..2d2de6bb0 --- /dev/null +++ b/src/states_screens/online_profile_achievements.hpp @@ -0,0 +1,67 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 __HEADER_ONLINE_PROFILE_ACHIEVEMENTS_HPP__ +#define __HEADER_ONLINE_PROFILE_ACHIEVEMENTS_HPP__ + +#include +#include + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "states_screens/online_profile_base.hpp" +#include "online/profile_manager.hpp" + + +namespace GUIEngine { class Widget; } + + +/** + * \brief Online profiel overview screen + * \ingroup states_screens + */ +class OnlineProfileAchievements : public OnlineProfileBase, public GUIEngine::ScreenSingleton +{ +private: + OnlineProfileAchievements(); + + GUIEngine::ListWidget * m_achievements_list_widget; + + int m_selected_achievement_index; + bool m_waiting_for_achievements; + +public: + friend class GUIEngine::ScreenSingleton; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + virtual void onUpdate(float delta, irr::video::IVideoDriver* driver) OVERRIDE; + + virtual void beforeAddingWidget() OVERRIDE; + + virtual void refreshAchievementsList() { m_waiting_for_achievements = true; } +}; + +#endif diff --git a/src/states_screens/online_profile_base.cpp b/src/states_screens/online_profile_base.cpp new file mode 100644 index 000000000..091bf0db4 --- /dev/null +++ b/src/states_screens/online_profile_base.cpp @@ -0,0 +1,109 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/online_profile_base.hpp" + +#include "guiengine/engine.hpp" +#include "guiengine/scalable_font.hpp" +#include "guiengine/screen.hpp" +#include "guiengine/widget.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "states_screens/online_profile_overview.hpp" +#include "states_screens/online_profile_friends.hpp" +#include "states_screens/online_profile_achievements.hpp" +#include "states_screens/online_profile_settings.hpp" + +#include +#include + +using namespace GUIEngine; +using namespace irr::core; +using namespace irr::gui; +using namespace Online; + + +OnlineProfileBase::OnlineProfileBase(const char* filename) : Screen(filename) +{ +} // OnlineProfileBase + +// ----------------------------------------------------------------------------- + +void OnlineProfileBase::loadedFromFile() +{ + m_profile_tabs = this->getWidget("profile_tabs"); + assert(m_profile_tabs != NULL); + m_header = this->getWidget("title"); + assert(m_header != NULL); + + m_overview_tab = (IconButtonWidget *) m_profile_tabs->findWidgetNamed("tab_overview"); + assert(m_overview_tab != NULL); + m_friends_tab = (IconButtonWidget *) m_profile_tabs->findWidgetNamed("tab_friends"); + assert(m_friends_tab != NULL); + m_achievements_tab = (IconButtonWidget *) m_profile_tabs->findWidgetNamed("tab_achievements"); + assert(m_achievements_tab != NULL); + m_settings_tab = (IconButtonWidget *) m_profile_tabs->findWidgetNamed("tab_settings"); + assert(m_settings_tab != NULL); + +} // loadedFromFile + +// ----------------------------------------------------------------------------- +void OnlineProfileBase::beforeAddingWidget() +{ + m_visiting_profile = ProfileManager::get()->getVisitingProfile(); + if (!m_visiting_profile->isCurrentUser()) + m_settings_tab->setVisible(false); +} + +// ----------------------------------------------------------------------------- +void OnlineProfileBase::init() +{ + Screen::init(); + + m_overview_tab->setTooltip( _("Overview") ); + m_friends_tab->setTooltip( _("Friends") ); + m_achievements_tab->setTooltip( _("Achievements") ); + m_settings_tab->setTooltip( _("Account Settings") ); + + if (m_visiting_profile->isCurrentUser()) + m_header->setText(_("Your profile"), false); + else + { + m_header->setText( m_visiting_profile->getUserName() + _("'s profile"), false); + } + +} // init + +// ----------------------------------------------------------------------------- + +void OnlineProfileBase::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + if (name == m_profile_tabs->m_properties[PROP_ID]) + { + std::string selection = ((RibbonWidget*)widget)->getSelectionIDString(PLAYER_ID_GAME_MASTER).c_str(); + + if (selection == m_overview_tab->m_properties[PROP_ID]) StateManager::get()->replaceTopMostScreen(OnlineProfileOverview::getInstance()); + else if (selection == m_friends_tab->m_properties[PROP_ID]) StateManager::get()->replaceTopMostScreen(OnlineProfileFriends::getInstance()); + else if (selection == m_achievements_tab->m_properties[PROP_ID]) StateManager::get()->replaceTopMostScreen(OnlineProfileAchievements::getInstance()); + else if (selection == m_settings_tab->m_properties[PROP_ID]) StateManager::get()->replaceTopMostScreen(OnlineProfileSettings::getInstance()); + } + else if (name == "back") + { + StateManager::get()->escapePressed(); + } +} // eventCallback + diff --git a/src/states_screens/online_profile_base.hpp b/src/states_screens/online_profile_base.hpp new file mode 100644 index 000000000..6b5ceba7a --- /dev/null +++ b/src/states_screens/online_profile_base.hpp @@ -0,0 +1,63 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 __HEADER_ONLINE_PROFILE_BASE_HPP__ +#define __HEADER_ONLINE_PROFILE_BASE_HPP__ + +#include +#include + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "online/profile_manager.hpp" + +namespace GUIEngine { class Widget; } + + +/** + * \brief Online profile base screen + * \ingroup states_screens + */ +class OnlineProfileBase : public GUIEngine::Screen +{ +protected: + OnlineProfileBase(const char* filename); + GUIEngine::LabelWidget * m_header; + GUIEngine::RibbonWidget* m_profile_tabs; + GUIEngine::IconButtonWidget * m_overview_tab; + GUIEngine::IconButtonWidget * m_friends_tab; + GUIEngine::IconButtonWidget * m_achievements_tab; + GUIEngine::IconButtonWidget * m_settings_tab; + + Online::Profile * m_visiting_profile; + +public: + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + virtual void beforeAddingWidget() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/online_profile_friends.cpp b/src/states_screens/online_profile_friends.cpp new file mode 100644 index 000000000..ec2db51c8 --- /dev/null +++ b/src/states_screens/online_profile_friends.cpp @@ -0,0 +1,142 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 "states_screens/online_profile_friends.hpp" + +#include "guiengine/engine.hpp" +#include "guiengine/scalable_font.hpp" +#include "guiengine/screen.hpp" +#include "guiengine/widget.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/online_user_search.hpp" +#include "states_screens/dialogs/user_info_dialog.hpp" +#include "utils/translation.hpp" +#include "online/messages.hpp" + +#include + +#include +#include + +using namespace GUIEngine; +using namespace irr::core; +using namespace irr::gui; +using namespace Online; + +DEFINE_SCREEN_SINGLETON( OnlineProfileFriends ); + +// ----------------------------------------------------------------------------- + +OnlineProfileFriends::OnlineProfileFriends() : OnlineProfileBase("online/profile_friends.stkgui") +{ + m_selected_friend_index = -1; +} // OnlineProfileFriends + +// ----------------------------------------------------------------------------- + +void OnlineProfileFriends::loadedFromFile() +{ + OnlineProfileBase::loadedFromFile(); + m_friends_list_widget = getWidget("friends_list"); + assert(m_friends_list_widget != NULL); + m_search_button_widget = getWidget("search_button"); + assert(m_search_button_widget != NULL); + m_search_box_widget = getWidget("search_box"); + assert(m_search_box_widget != NULL); + +} // loadedFromFile + +// ---------------------------------------------------------------------------- + +void OnlineProfileFriends::beforeAddingWidget() +{ + OnlineProfileBase::beforeAddingWidget(); + m_friends_list_widget->clearColumns(); + m_friends_list_widget->addColumn( _("Username"), 2 ); + if(m_visiting_profile->isCurrentUser()) + { + m_friends_list_widget->addColumn( _("Since"), 1 ); + m_friends_list_widget->addColumn( _("Status"), 2 ); + } +} + +// ----------------------------------------------------------------------------- + +void OnlineProfileFriends::init() +{ + OnlineProfileBase::init(); + m_profile_tabs->select( m_friends_tab->m_properties[PROP_ID], PLAYER_ID_GAME_MASTER ); + assert(m_visiting_profile != NULL); + m_visiting_profile->fetchFriends(); + m_waiting_for_friends = true; + m_friends_list_widget->clear(); + m_friends_list_widget->addItem("loading", Messages::fetchingFriends()); +} // init +// ----------------------------------------------------------------------------- + +void OnlineProfileFriends::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + OnlineProfileBase::eventCallback( widget, name, playerID); + if (name == m_search_button_widget->m_properties[GUIEngine::PROP_ID]) + { + OnlineUserSearch * instance = OnlineUserSearch::getInstance(); + instance->setSearchString(m_search_box_widget->getText().trim()); + StateManager::get()->replaceTopMostScreen(instance); + } + else if (name == m_friends_list_widget->m_properties[GUIEngine::PROP_ID]) + { + m_selected_friend_index = m_friends_list_widget->getSelectionID(); + new UserInfoDialog(m_visiting_profile->getFriends()[m_selected_friend_index]); + } +} // eventCallback + +// ---------------------------------------------------------------------------- +void OnlineProfileFriends::onUpdate(float delta, irr::video::IVideoDriver* driver) +{ + if(m_waiting_for_friends) + { + if(m_visiting_profile->isReady()) + { + m_friends_list_widget->clear(); + for(unsigned int i = 0; i < m_visiting_profile->getFriends().size(); i++) + { + PtrVector * row = new PtrVector; + Profile * friend_profile = ProfileManager::get()->getProfileByID(m_visiting_profile->getFriends()[i]); + row->push_back(new GUIEngine::ListWidget::ListCell(friend_profile->getUserName(),-1,2)); + if(m_visiting_profile->isCurrentUser()) + { + Profile::RelationInfo * relation_info = friend_profile->getRelationInfo(); + row->push_back(new GUIEngine::ListWidget::ListCell(relation_info->getDate(),-1,1, true)); + irr::core::stringw status(""); + if(relation_info->isPending()) + { + status = (relation_info->isAsker() ? _("New Request") : _("Pending")); + } + else + status = (relation_info->isOnline() ? _("Online") : _("Offline")); + row->push_back(new GUIEngine::ListWidget::ListCell(status,-1,2, true)); + } + m_friends_list_widget->addItem("friend", row); + } + m_waiting_for_friends = false; + } + else + { + m_friends_list_widget->renameItem("loading", Messages::fetchingFriends()); + } + } +} diff --git a/src/states_screens/online_profile_friends.hpp b/src/states_screens/online_profile_friends.hpp new file mode 100644 index 000000000..0c74e2833 --- /dev/null +++ b/src/states_screens/online_profile_friends.hpp @@ -0,0 +1,69 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 __HEADER_ONLINE_PROFILE_FRIENDS_HPP__ +#define __HEADER_ONLINE_PROFILE_FRIENDS_HPP__ + +#include +#include + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "states_screens/online_profile_base.hpp" +#include "online/profile_manager.hpp" + + +namespace GUIEngine { class Widget; } + + +/** + * \brief Online profiel overview screen + * \ingroup states_screens + */ +class OnlineProfileFriends : public OnlineProfileBase, public GUIEngine::ScreenSingleton +{ +private: + OnlineProfileFriends(); + + GUIEngine::ListWidget * m_friends_list_widget; + GUIEngine::ButtonWidget * m_search_button_widget; + GUIEngine::TextBoxWidget * m_search_box_widget; + + int m_selected_friend_index; + bool m_waiting_for_friends; + +public: + friend class GUIEngine::ScreenSingleton; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + virtual void onUpdate(float delta, irr::video::IVideoDriver* driver) OVERRIDE; + + virtual void beforeAddingWidget() OVERRIDE; + + virtual void refreshFriendsList() {m_waiting_for_friends = true; } +}; + +#endif diff --git a/src/states_screens/online_profile_overview.cpp b/src/states_screens/online_profile_overview.cpp new file mode 100644 index 000000000..128437f47 --- /dev/null +++ b/src/states_screens/online_profile_overview.cpp @@ -0,0 +1,67 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010 Glenn De Jonghe +// +// 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 "states_screens/online_profile_overview.hpp" + +#include "guiengine/engine.hpp" +#include "guiengine/scalable_font.hpp" +#include "guiengine/screen.hpp" +#include "guiengine/widget.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" + +#include + +#include +#include + +using namespace GUIEngine; +using namespace irr::core; +using namespace irr::gui; +using namespace Online; + +DEFINE_SCREEN_SINGLETON( OnlineProfileOverview ); + +// ----------------------------------------------------------------------------- + +OnlineProfileOverview::OnlineProfileOverview() : OnlineProfileBase("online/profile_overview.stkgui") +{ +} // OnlineProfileOverview + +// ----------------------------------------------------------------------------- + +void OnlineProfileOverview::loadedFromFile() +{ + OnlineProfileBase::loadedFromFile(); + +} // loadedFromFile + +// ----------------------------------------------------------------------------- + +void OnlineProfileOverview::init() +{ + OnlineProfileBase::init(); + m_profile_tabs->select( m_overview_tab->m_properties[PROP_ID], PLAYER_ID_GAME_MASTER ); +} // init + +// ----------------------------------------------------------------------------- + +void OnlineProfileOverview::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + OnlineProfileBase::eventCallback( widget, name, playerID); +} // eventCallback + diff --git a/src/states_screens/online_profile_overview.hpp b/src/states_screens/online_profile_overview.hpp new file mode 100644 index 000000000..6ad7619e1 --- /dev/null +++ b/src/states_screens/online_profile_overview.hpp @@ -0,0 +1,55 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 __HEADER_ONLINE_PROFILE_OVERVIEW_HPP__ +#define __HEADER_ONLINE_PROFILE_OVERVIEW_HPP__ + +#include +#include + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "states_screens/online_profile_base.hpp" + +namespace GUIEngine { class Widget; } + + +/** + * \brief Online profiel overview screen + * \ingroup states_screens + */ +class OnlineProfileOverview : public OnlineProfileBase, public GUIEngine::ScreenSingleton +{ +protected: + OnlineProfileOverview(); + +public: + friend class GUIEngine::ScreenSingleton; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/online_profile_settings.cpp b/src/states_screens/online_profile_settings.cpp new file mode 100644 index 000000000..06abe7bde --- /dev/null +++ b/src/states_screens/online_profile_settings.cpp @@ -0,0 +1,73 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010 Glenn De Jonghe +// +// 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 "states_screens/online_profile_settings.hpp" + +#include "guiengine/engine.hpp" +#include "guiengine/scalable_font.hpp" +#include "guiengine/screen.hpp" +#include "guiengine/widget.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/dialogs/change_password_dialog.hpp" +#include "utils/translation.hpp" + +#include + +#include +#include + +using namespace GUIEngine; +using namespace irr::core; +using namespace irr::gui; +using namespace Online; + +DEFINE_SCREEN_SINGLETON( OnlineProfileSettings ); + +// ----------------------------------------------------------------------------- + +OnlineProfileSettings::OnlineProfileSettings() : OnlineProfileBase("online/profile_settings.stkgui") +{ +} // OnlineProfileSettings + +// ----------------------------------------------------------------------------- + +void OnlineProfileSettings::loadedFromFile() +{ + OnlineProfileBase::loadedFromFile(); + m_change_password_button = this->getWidget("change_password_button"); + assert(m_change_password_button != NULL); +} // loadedFromFile + +// ----------------------------------------------------------------------------- + +void OnlineProfileSettings::init() +{ + OnlineProfileBase::init(); + m_profile_tabs->select( m_settings_tab->m_properties[PROP_ID], PLAYER_ID_GAME_MASTER ); +} // init + +// ----------------------------------------------------------------------------- + +void OnlineProfileSettings::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + OnlineProfileBase::eventCallback( widget, name, playerID); + if (name == m_change_password_button->m_properties[GUIEngine::PROP_ID]) + { + new ChangePasswordDialog(); + } +} // eventCallback + diff --git a/src/states_screens/online_profile_settings.hpp b/src/states_screens/online_profile_settings.hpp new file mode 100644 index 000000000..36fbd2669 --- /dev/null +++ b/src/states_screens/online_profile_settings.hpp @@ -0,0 +1,56 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 __HEADER_ONLINE_PROFILE_SETTINGS_HPP__ +#define __HEADER_ONLINE_PROFILE_SETTINGS_HPP__ + +#include +#include + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "states_screens/online_profile_base.hpp" + +namespace GUIEngine { class Widget; } + + +/** + * \brief Online profiel overview screen + * \ingroup states_screens + */ +class OnlineProfileSettings : public OnlineProfileBase, public GUIEngine::ScreenSingleton +{ +protected: + OnlineProfileSettings(); + GUIEngine::ButtonWidget * m_change_password_button; + +public: + friend class GUIEngine::ScreenSingleton; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/online_screen.cpp b/src/states_screens/online_screen.cpp new file mode 100644 index 000000000..cc9157904 --- /dev/null +++ b/src/states_screens/online_screen.cpp @@ -0,0 +1,298 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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. + +#define DEBUG_MENU_ITEM 0 + +#include "states_screens/online_screen.hpp" + +#include +#include + +#include "audio/sfx_manager.hpp" +#include "graphics/irr_driver.hpp" +#include "guiengine/scalable_font.hpp" +#include "input/device_manager.hpp" +#include "input/input_manager.hpp" +#include "io/file_manager.hpp" +#include "main_loop.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/dialogs/message_dialog.hpp" +#include "states_screens/dialogs/login_dialog.hpp" +#include "states_screens/dialogs/registration_dialog.hpp" +#include "states_screens/networking_lobby.hpp" +#include "states_screens/server_selection.hpp" +#include "states_screens/create_server_screen.hpp" +#include "states_screens/online_profile_overview.hpp" +#include "online/servers_manager.hpp" +#include "online/messages.hpp" +#include "online/profile_manager.hpp" +#include "online/request.hpp" +#include "modes/demo_world.hpp" + +#include "network/protocol_manager.hpp" +#include "network/protocols/connect_to_server.hpp" + +#include "network/protocol_manager.hpp" +#include "network/protocols/connect_to_server.hpp" + + +using namespace GUIEngine; +using namespace Online; + +DEFINE_SCREEN_SINGLETON( OnlineScreen ); + +// ---------------------------------------------------------------------------- + +OnlineScreen::OnlineScreen() : Screen("online/main.stkgui") +{ + m_recorded_state = CurrentUser::US_SIGNED_OUT; +} // OnlineScreen + +// ---------------------------------------------------------------------------- + +OnlineScreen::~OnlineScreen() +{ +} + +// ---------------------------------------------------------------------------- + +void OnlineScreen::loadedFromFile() +{ + m_back_widget = getWidget("back"); + assert(m_back_widget != NULL); + + m_top_menu_widget = getWidget("menu_toprow"); + assert(m_top_menu_widget != NULL); + m_quick_play_widget = (IconButtonWidget *) m_top_menu_widget->findWidgetNamed("quick_play"); + assert(m_quick_play_widget != NULL); + m_find_server_widget = (IconButtonWidget *) m_top_menu_widget->findWidgetNamed("find_server"); + assert(m_find_server_widget != NULL); + m_create_server_widget = (IconButtonWidget *) m_top_menu_widget->findWidgetNamed("create_server"); + assert(m_create_server_widget != NULL); + + m_online_status_widget = getWidget("online_status"); + assert(m_online_status_widget != NULL); + + m_bottom_menu_widget = getWidget("menu_bottomrow"); + assert(m_bottom_menu_widget != NULL); + m_sign_in_widget = (IconButtonWidget *) m_bottom_menu_widget->findWidgetNamed("sign_in"); + assert(m_sign_in_widget != NULL); + m_register_widget = (IconButtonWidget *) m_bottom_menu_widget->findWidgetNamed("register"); + assert(m_register_widget != NULL); + m_profile_widget = (IconButtonWidget *) m_bottom_menu_widget->findWidgetNamed("profile"); + assert(m_profile_widget != NULL); + m_sign_out_widget = (IconButtonWidget *) m_bottom_menu_widget->findWidgetNamed("sign_out"); + assert(m_sign_out_widget != NULL); + +} // loadedFromFile + +// ---------------------------------------------------------------------------- +bool OnlineScreen::hasStateChanged() +{ + CurrentUser::UserState previous_state = m_recorded_state; + m_recorded_state = CurrentUser::get()->getUserState(); + if (previous_state != m_recorded_state) + return true; + return false; +} + +// ---------------------------------------------------------------------------- +void OnlineScreen::beforeAddingWidget() +{ + //Set everything that could be set invisible or deactivated, to active and visible + m_bottom_menu_widget->setVisible(true); + m_top_menu_widget->setVisible(true); + hasStateChanged(); + if (m_recorded_state == CurrentUser::US_SIGNED_IN) + { + m_register_widget->setVisible(false); + m_sign_in_widget->setVisible(false); + } + else if (m_recorded_state == CurrentUser::US_SIGNED_OUT || m_recorded_state == CurrentUser::US_SIGNING_IN || m_recorded_state == CurrentUser::US_SIGNING_OUT) + { + m_quick_play_widget->setDeactivated(); + m_find_server_widget->setDeactivated(); + m_create_server_widget->setDeactivated(); + m_sign_out_widget->setVisible(false); + m_profile_widget->setVisible(false); + if(m_recorded_state == CurrentUser::US_SIGNING_IN || m_recorded_state == CurrentUser::US_SIGNING_OUT) + { + m_register_widget->setDeactivated(); + m_sign_in_widget->setDeactivated(); + } + } + else if (m_recorded_state == CurrentUser::US_GUEST) + { + m_find_server_widget->setDeactivated(); + m_create_server_widget->setDeactivated(); + m_sign_in_widget->setVisible(false); + m_profile_widget->setVisible(false); + } + +} // beforeAddingWidget + + + +// ---------------------------------------------------------------------------- +void OnlineScreen::init() +{ + Screen::init(); + setInitialFocus(); + DemoWorld::resetIdleTime(); + m_online_status_widget->setText(Messages::signedInAs(CurrentUser::get()->getUserName()), false); +} // init + +// ---------------------------------------------------------------------------- +void OnlineScreen::onUpdate(float delta, irr::video::IVideoDriver* driver) +{ + if (hasStateChanged()) + { + GUIEngine::reshowCurrentScreen(); + return; + } + + if (m_recorded_state == CurrentUser::US_SIGNING_IN) + { + m_online_status_widget->setText(Messages::signingIn(), false); + } + else if (m_recorded_state == CurrentUser::US_SIGNING_OUT) + { + m_online_status_widget->setText(Messages::signingOut(), false); + } +} // onUpdate + +// ---------------------------------------------------------------------------- + +void OnlineScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + if (name == m_back_widget->m_properties[PROP_ID]) + { + StateManager::get()->escapePressed(); + return; + } + + RibbonWidget* ribbon = dynamic_cast(widget); + if (ribbon == NULL) return; + std::string selection = ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER); + + if (selection == m_sign_in_widget->m_properties[PROP_ID]) + { + new LoginDialog(LoginDialog::Normal); + } + else if (selection == m_sign_out_widget->m_properties[PROP_ID]) + { + CurrentUser::get()->requestSignOut(); + } + else if (selection == m_profile_widget->m_properties[PROP_ID]) + { + ProfileManager::get()->setVisiting(CurrentUser::get()->getID()); + StateManager::get()->pushScreen(OnlineProfileOverview::getInstance()); + } + else if (selection == m_register_widget->m_properties[PROP_ID]) + { + new RegistrationDialog(); + } + else if (selection == m_find_server_widget->m_properties[PROP_ID]) + { + StateManager::get()->pushScreen(ServerSelection::getInstance()); + } + else if (selection == m_create_server_widget->m_properties[PROP_ID]) + { + StateManager::get()->pushScreen(CreateServerScreen::getInstance()); + } + else if (selection == m_quick_play_widget->m_properties[PROP_ID]) + { + //FIXME temporary and the request join + join sequence should be placed in one method somewhere + // refresh server list + Online::ServersManager::RefreshRequest* request = ServersManager::get()->refreshRequest(false); + if (request != NULL) // consider request done + { + Online::HTTPManager::get()->synchronousRequest(request); + delete request; + } + else + { + Log::error("OnlineScreen", "Could not get the server list."); + return; + } + // select first one + const Server * server = ServersManager::get()->getQuickPlay(); + + Online::CurrentUser::ServerJoinRequest* request2 = Online::CurrentUser::get()->requestServerJoin( server->getServerId(), false); + if (request2) + { + Online::HTTPManager::get()->synchronousRequest(request2); + if (request2->isSuccess()) + { + delete request2; + StateManager::get()->pushScreen(NetworkingLobby::getInstance()); + ProtocolManager::getInstance()->requestStart(new ConnectToServer(server->getServerId(), server->getHostId())); + } + else + { + sfx_manager->quickSound( "anvil" ); + } + } + else + { + sfx_manager->quickSound( "anvil" ); + } + } + +} // eventCallback + +// ---------------------------------------------------------------------------- +void OnlineScreen::tearDown() +{ +} + +// ---------------------------------------------------------------------------- +void OnlineScreen::onDisabledItemClicked(const std::string& item) +{ + if (item == "find_server") + { + new LoginDialog(LoginDialog::Registration_Required); + } + else if (item =="create_server") + { + new LoginDialog(LoginDialog::Registration_Required); + } + else if (item == "quick_play") + { + new LoginDialog(LoginDialog::Signing_In_Required); + } +} // onDisabledItemClicked + +// ---------------------------------------------------------------------------- +void OnlineScreen::setInitialFocus() +{ + if(m_recorded_state == CurrentUser::US_SIGNED_IN) + m_top_menu_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + else + m_bottom_menu_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + +} // setInitialFocus + +// ---------------------------------------------------------------------------- +void OnlineScreen::onDialogClose() +{ + if (hasStateChanged()) + GUIEngine::reshowCurrentScreen(); + else + setInitialFocus(); +} // onDialogClose() + diff --git a/src/states_screens/online_screen.hpp b/src/states_screens/online_screen.hpp new file mode 100644 index 000000000..97b8ace01 --- /dev/null +++ b/src/states_screens/online_screen.hpp @@ -0,0 +1,92 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ONLINE_SCREEN_HPP +#define HEADER_ONLINE_SCREEN_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "online/current_user.hpp" +#include "utils/ptr_vector.hpp" + +namespace GUIEngine { class Widget; class ListWidget; } + +/** + * \brief Handles the main menu + * \ingroup states_screens + */ +class OnlineScreen : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton +{ +private: + friend class GUIEngine::ScreenSingleton; + + OnlineScreen(); + ~OnlineScreen(); + + GUIEngine::IconButtonWidget * m_back_widget; + + GUIEngine::RibbonWidget * m_top_menu_widget; + GUIEngine::IconButtonWidget * m_quick_play_widget; + GUIEngine::IconButtonWidget * m_find_server_widget; + GUIEngine::IconButtonWidget * m_create_server_widget; + + GUIEngine::LabelWidget * m_online_status_widget; + + GUIEngine::RibbonWidget * m_bottom_menu_widget; + GUIEngine::IconButtonWidget * m_sign_in_widget; + GUIEngine::IconButtonWidget * m_register_widget; + GUIEngine::IconButtonWidget * m_profile_widget; + GUIEngine::IconButtonWidget * m_sign_out_widget; + + Online::CurrentUser::UserState m_recorded_state; + + /** \brief Checks if the recorded state differs from the actual state and sets it. */ + bool hasStateChanged(); + /** \brief Sets which widget has to be focused. Depends on the user state. */ + void setInitialFocus(); + +public: + + virtual void onUpdate(float delta, irr::video::IVideoDriver* driver) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void tearDown() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void onDisabledItemClicked(const std::string& item) OVERRIDE; + + /** \brief Implements the callback when a dialog gets closed. */ + virtual void onDialogClose() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/online_user_search.cpp b/src/states_screens/online_user_search.cpp new file mode 100644 index 000000000..e97d574a2 --- /dev/null +++ b/src/states_screens/online_user_search.cpp @@ -0,0 +1,212 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010 Lucas Baudin, 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 "states_screens/online_user_search.hpp" + +#include +#include + +#include "guiengine/modaldialog.hpp" +#include "states_screens/dialogs/user_info_dialog.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/dialogs/message_dialog.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "online/messages.hpp" +#include "online/current_user.hpp" +#include "audio/sfx_manager.hpp" + +using namespace Online; + +DEFINE_SCREEN_SINGLETON( OnlineUserSearch ); + +// ---------------------------------------------------------------------------- + +OnlineUserSearch::OnlineUserSearch() : Screen("online/user_search.stkgui") +{ + m_selected_index = -1; + m_search_request = NULL; + m_search_string = ""; + m_last_search_string = ""; +} // OnlineUserSearch + +// ---------------------------------------------------------------------------- + +OnlineUserSearch::~OnlineUserSearch() +{ +} // OnlineUserSearch + +// ---------------------------------------------------------------------------- + +void OnlineUserSearch::tearDown() +{ + delete m_search_request; + m_search_request = NULL; +} // tearDown + + +// ---------------------------------------------------------------------------- + +void OnlineUserSearch::parseResult(const XMLNode * input) +{ + m_users.clear(); + const XMLNode * users_xml = input->getNode("users"); + for (unsigned int i = 0; i < users_xml->getNumNodes(); i++) + { + Profile * profile = new Profile(users_xml->getNode(i)); + ProfileManager::get()->addToCache(profile); + m_users.push_back(profile->getID()); + } +} + +void OnlineUserSearch::showList() +{ + m_user_list_widget->clear(); + for(unsigned int i=0; i < m_users.size(); i++) + { + PtrVector * row = new PtrVector; + Profile * profile = ProfileManager::get()->getProfileByID(m_users[i]); + assert(profile != NULL); + row->push_back(new GUIEngine::ListWidget::ListCell(profile->getUserName(),-1,3)); + m_user_list_widget->addItem("user", row); + } +} + +// ---------------------------------------------------------------------------- + +void OnlineUserSearch::search() +{ + if ( m_search_string != "" && m_last_search_string != m_search_string ) + m_search_request = CurrentUser::get()->requestUserSearch(m_search_string); + else + m_fake_refresh = true; + m_user_list_widget->clear(); + m_user_list_widget->addItem("spacer", L""); + m_user_list_widget->addItem("loading", Messages::searching()); + m_back_widget->setDeactivated(); + m_search_box_widget->setDeactivated(); + m_search_button_widget->setDeactivated(); +} + + +// ---------------------------------------------------------------------------- + +void OnlineUserSearch::loadedFromFile() +{ + m_back_widget = getWidget("back"); + assert(m_back_widget != NULL); + m_search_button_widget = getWidget("search_button"); + assert(m_search_button_widget != NULL); + m_search_box_widget = getWidget("search_box"); + assert(m_search_box_widget != NULL); + m_user_list_widget = getWidget("user_list"); + assert(m_user_list_widget != NULL); +} // loadedFromFile + + +// ---------------------------------------------------------------------------- + +void OnlineUserSearch::beforeAddingWidget() +{ + m_user_list_widget->clearColumns(); + m_user_list_widget->addColumn( _("Username"), 3 ); +} +// ---------------------------------------------------------------------------- + +void OnlineUserSearch::init() +{ + Screen::init(); + search(); + m_fake_refresh = false; + m_search_box_widget->setText(m_search_string); +} // init + +// ---------------------------------------------------------------------------- +void OnlineUserSearch::eventCallback( GUIEngine::Widget* widget, const std::string& name, const int playerID) +{ + if (name == m_back_widget->m_properties[GUIEngine::PROP_ID]) + { + StateManager::get()->escapePressed(); + } + else if (name == m_user_list_widget->m_properties[GUIEngine::PROP_ID]) + { + m_selected_index = m_user_list_widget->getSelectionID(); + new UserInfoDialog(m_users[m_selected_index]); + } + else if (name == m_search_button_widget->m_properties[GUIEngine::PROP_ID]) + { + m_last_search_string = m_search_string; + m_search_string = m_search_box_widget->getText().trim(); + search(); + } + +} // eventCallback + +// ---------------------------------------------------------------------------- +/** Selects the last selected item on the list (which is the item that + * is just being installed) again. This function is used from the + * addons_loading screen: when it is closed, it will reset the + * select item so that people can keep on installing from that + * point on. +*/ +void OnlineUserSearch::setLastSelected() //FIXME actually use this here and in server selection +{ + if(m_selected_index>-1) + { + m_user_list_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + m_user_list_widget->setSelectionID(m_selected_index); + } +} // setLastSelected + +// ---------------------------------------------------------------------------- + +void OnlineUserSearch::onUpdate(float dt, irr::video::IVideoDriver*) +{ + if(m_search_request != NULL) + { + if(m_search_request->isDone()) + { + if(m_search_request->isSuccess()) + { + parseResult(m_search_request->getResult()); + showList(); + } + else + { + sfx_manager->quickSound( "anvil" ); + new MessageDialog(m_search_request->getInfo()); + } + delete m_search_request; + m_search_request = NULL; + m_back_widget->setActivated(); + m_search_box_widget->setActivated(); + m_search_button_widget->setActivated(); + } + else + { + m_user_list_widget->renameItem("loading", Messages::searching()); + } + } + else if(m_fake_refresh) + { + showList(); + m_fake_refresh = false; + m_back_widget->setActivated(); + m_search_box_widget->setActivated(); + m_search_button_widget->setActivated(); + } +} // onUpdate diff --git a/src/states_screens/online_user_search.hpp b/src/states_screens/online_user_search.hpp new file mode 100644 index 000000000..263c8a694 --- /dev/null +++ b/src/states_screens/online_user_search.hpp @@ -0,0 +1,85 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_ONLINE_USER_SEARCH_HPP +#define HEADER_ONLINE_USER_SEARCH_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "online/profile.hpp" +#include "online/request.hpp" +#include "utils/ptr_vector.hpp" + +namespace GUIEngine { class Widget; } + +/** + * \brief + * \ingroup + */ +class OnlineUserSearch : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; + +private: + OnlineUserSearch(); + ~OnlineUserSearch(); + + GUIEngine::IconButtonWidget * m_back_widget; + GUIEngine::ButtonWidget * m_search_button_widget; + GUIEngine::TextBoxWidget * m_search_box_widget; + GUIEngine::ListWidget * m_user_list_widget; + + /** The currently selected index, used to re-select this item after + * addons_loading is being displayed. */ + int m_selected_index; + irr::core::stringw m_search_string; + irr::core::stringw m_last_search_string; + Online::Profile::IDList m_users; + const Online::XMLRequest * m_search_request; + bool m_fake_refresh; + + void parseResult(const XMLNode * input); + void showList(); + void search(); + + +public: + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + virtual void init() OVERRIDE; + + virtual void tearDown() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void onUpdate(float dt, irr::video::IVideoDriver*) OVERRIDE; + + void setLastSelected(); + void setSearchString(const irr::core::stringw & search_string) {m_search_string = search_string;} + +}; + +#endif diff --git a/src/states_screens/options_screen_audio.cpp b/src/states_screens/options_screen_audio.cpp index 1b267ca29..5d6de54e0 100644 --- a/src/states_screens/options_screen_audio.cpp +++ b/src/states_screens/options_screen_audio.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_audio.hpp b/src/states_screens/options_screen_audio.hpp index 1a4f4380a..2b6468de0 100644 --- a/src/states_screens/options_screen_audio.hpp +++ b/src/states_screens/options_screen_audio.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_input.cpp b/src/states_screens/options_screen_input.cpp index 490160abb..4a4f74d69 100644 --- a/src/states_screens/options_screen_input.cpp +++ b/src/states_screens/options_screen_input.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_input.hpp b/src/states_screens/options_screen_input.hpp index d7dd1fb40..25e9a7e9b 100644 --- a/src/states_screens/options_screen_input.hpp +++ b/src/states_screens/options_screen_input.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_input2.cpp b/src/states_screens/options_screen_input2.cpp index cc74b5ec7..e8e49d033 100644 --- a/src/states_screens/options_screen_input2.cpp +++ b/src/states_screens/options_screen_input2.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_input2.hpp b/src/states_screens/options_screen_input2.hpp index bfa2f7e8a..5bf3aeab1 100644 --- a/src/states_screens/options_screen_input2.hpp +++ b/src/states_screens/options_screen_input2.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_players.cpp b/src/states_screens/options_screen_players.cpp index 8b5044612..5f7ca3832 100644 --- a/src/states_screens/options_screen_players.cpp +++ b/src/states_screens/options_screen_players.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_players.hpp b/src/states_screens/options_screen_players.hpp index 3261bc402..381be34a2 100644 --- a/src/states_screens/options_screen_players.hpp +++ b/src/states_screens/options_screen_players.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Marianne Gagnon +// Copyright (C) 2010-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_ui.cpp b/src/states_screens/options_screen_ui.cpp index 41ce26541..f64e978f9 100644 --- a/src/states_screens/options_screen_ui.cpp +++ b/src/states_screens/options_screen_ui.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -125,14 +125,6 @@ void OptionsScreenUI::init() assert( news != NULL ); news->setState( UserConfigParams::m_internet_status ==INetworkHttp::IPERM_ALLOWED ); - CheckBoxWidget* min_gui = getWidget("minimal-racegui"); - assert( min_gui != NULL ); - min_gui->setState( UserConfigParams::m_minimal_race_gui); - if (StateManager::get()->getGameState() == GUIEngine::INGAME_MENU) - min_gui->setDeactivated(); - else - min_gui->setActivated(); - // --- select the right skin in the spinner bool currSkinFound = false; @@ -167,8 +159,7 @@ void OptionsScreenUI::init() { std::string code_name = (*lang_list)[n]; std::string nice_name = tinygettext::Language::from_name(code_name.c_str()).get_name(); - list_widget->addItem(code_name, core::stringw(code_name.c_str()) + " (" + - nice_name.c_str() + ")"); + list_widget->addItem(code_name, nice_name.c_str()); } list_widget->setSelectionID( list_widget->getItemID(UserConfigParams::m_language) ); @@ -236,13 +227,6 @@ void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, con // a race condition can be introduced resulting in a crash). INetworkHttp::get()->startNetworkThread(); } - else if (name=="minimal-racegui") - { - CheckBoxWidget* min_gui = getWidget("minimal-racegui"); - assert( min_gui != NULL ); - UserConfigParams::m_minimal_race_gui = - !UserConfigParams::m_minimal_race_gui; - } else if (name == "language") { ListWidget* list_widget = getWidget("language"); diff --git a/src/states_screens/options_screen_ui.hpp b/src/states_screens/options_screen_ui.hpp index d88eccc2c..91d419413 100644 --- a/src/states_screens/options_screen_ui.hpp +++ b/src/states_screens/options_screen_ui.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/options_screen_video.cpp b/src/states_screens/options_screen_video.cpp index 12331bb1d..cf38c1a4e 100644 --- a/src/states_screens/options_screen_video.cpp +++ b/src/states_screens/options_screen_video.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -45,15 +45,18 @@ using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( OptionsScreenVideo ); // Look-up table for GFX levels -const bool GFX [] = {false, true, true, true, true, true, true, true}; -const int GFX_ANIM_KARTS[] = {0, 0, 1, 2, 2, 2, 2, 2}; -const bool GFX_WEATHER [] = {false, false, false, false, true, true, true, true}; -const int GFX_ANTIALIAS [] = {0, 0, 0, 0, 0, 2, 2, 3}; -const bool GFX_POSTPROCESSING[] = - {false, false, false, false, false, false, true, true}; -const bool GFX_PIXEL_SHADERS[] = - {false, false, false, false, true, true, true, true}; -const int GFX_LEVEL_AMOUNT = 8; +static const bool GFX [] = {false, true, true, true, true, true, true}; +static const int GFX_ANIM_KARTS[] = {0, 1, 2, 2, 2, 2, 2 }; +static const bool GFX_WEATHER [] = {false, false, true, true, true, true, true}; +static const bool GFX_MOTIONBLUR[] = {false, false, false, false, true, true, true}; +static const bool GFX_PIXEL_SHADERS[] = + {false, false, true, true, true, true, true}; +static const bool GFX_MLAA[] = {false, false, false, false, false, true, true}; +static const int GFX_SSAO[] = {0, 0, 0, 1, 1, 1, 2 }; +static const int GFX_SHADOWS[] = {0, 0, 0, 1, 1, 2, 2 }; + + +static const int GFX_LEVEL_AMOUNT = 7; // ---------------------------------------------------------------------------- @@ -102,11 +105,6 @@ void OptionsScreenVideo::init() assert( vsync != NULL ); vsync->setState( UserConfigParams::m_vsync ); - GUIEngine::CheckBoxWidget* fbos = - getWidget("fbos"); - assert( fbos != NULL ); - fbos->setState( UserConfigParams::m_fbo ); - // ---- video modes DynamicRibbonWidget* res = getWidget("resolutions"); @@ -314,8 +312,11 @@ void OptionsScreenVideo::updateGfxSlider() if (UserConfigParams::m_show_steering_animations == GFX_ANIM_KARTS[l]&& UserConfigParams::m_graphical_effects == GFX[l] && UserConfigParams::m_weather_effects == GFX_WEATHER[l] && - UserConfigParams::m_antialiasing == GFX_ANTIALIAS[l] && - UserConfigParams::m_postprocess_enabled == GFX_POSTPROCESSING[l] && + //UserConfigParams::m_antialiasing == GFX_ANTIALIAS[l] && + UserConfigParams::m_motionblur == GFX_MOTIONBLUR[l] && + UserConfigParams::m_mlaa == GFX_MLAA[l] && + UserConfigParams::m_ssao == GFX_SSAO[l] && + UserConfigParams::m_shadows == GFX_SHADOWS[l] && UserConfigParams::m_pixel_shaders == GFX_PIXEL_SHADERS[l]) { gfx->setValue(l+1); @@ -345,17 +346,18 @@ void OptionsScreenVideo::updateTooltip() //I18N: in the graphical options tooltip; // indicates a graphical feature is enabled - core::stringw enabled = _LTR("Enabled"); + const core::stringw enabled = _LTR("Enabled"); //I18N: in the graphical options tooltip; // indicates a graphical feature is disabled - core::stringw disabled = _LTR("Disabled"); + const core::stringw disabled = _LTR("Disabled"); //I18N: if all kart animations are enabled - core::stringw all = _LTR("All"); + const core::stringw all = _LTR("All"); //I18N: if some kart animations are enabled - core::stringw me = _LTR("Me Only"); + const core::stringw me = _LTR("Me Only"); //I18N: if no kart animations are enabled - core::stringw none = _LTR("None"); + const core::stringw none = _LTR("None"); + /* core::stringw antialias_label; switch ((int)UserConfigParams::m_antialiasing) { @@ -368,7 +370,11 @@ void OptionsScreenVideo::updateTooltip() case 3: antialias_label = L"x8"; break; } + */ + //I18N: in graphical options + tooltip = tooltip + L"\n" + _("Pixel shaders : %s", + UserConfigParams::m_pixel_shaders ? enabled : disabled); //I18N: in graphical options tooltip = _("Animated Scenery : %s", UserConfigParams::m_graphical_effects ? enabled : disabled); @@ -381,14 +387,25 @@ void OptionsScreenVideo::updateTooltip() ? all : (UserConfigParams::m_show_steering_animations == 1 ? me : none)); //I18N: in graphical options - tooltip = tooltip + L"\n" + _("Anti-aliasing (requires restart) : %s", - antialias_label); + //tooltip = tooltip + L"\n" + _("Anti-aliasing (requires restart) : %s", + // antialias_label); //I18N: in graphical options - tooltip = tooltip + L"\n" + _("Pixel shaders : %s", - UserConfigParams::m_pixel_shaders ? enabled : disabled); + tooltip = tooltip + L"\n" + _("Motion blur: %s", + UserConfigParams::m_motionblur ? enabled : disabled); //I18N: in graphical options - tooltip = tooltip + L"\n" + _("Post-processing (motion blur) : %s", - UserConfigParams::m_postprocess_enabled ? enabled : disabled); + tooltip = tooltip + L"\n" + _("Anti-aliasing : %s", + UserConfigParams::m_mlaa ? enabled : disabled); + //I18N: in graphical options + tooltip = tooltip + L"\n" + _("Ambient occlusion : %s", + UserConfigParams::m_ssao == 1 ? "low" : UserConfigParams::m_ssao == 2 ? + "high" : disabled); + //I18N: in graphical options + tooltip = tooltip + L"\n" + _("Shadows: %s", + UserConfigParams::m_shadows == 1 ? "low" : UserConfigParams::m_shadows == 2 ? + "high" : disabled); + + + gfx->setTooltip(tooltip); } // updateTooltip @@ -460,9 +477,12 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name, UserConfigParams::m_show_steering_animations = GFX_ANIM_KARTS[level-1]; UserConfigParams::m_graphical_effects = GFX[level-1]; UserConfigParams::m_weather_effects = GFX_WEATHER[level-1]; - UserConfigParams::m_antialiasing = GFX_ANTIALIAS[level-1]; - UserConfigParams::m_postprocess_enabled = GFX_POSTPROCESSING[level-1]; + //UserConfigParams::m_antialiasing = GFX_ANTIALIAS[level-1]; + UserConfigParams::m_motionblur = GFX_MOTIONBLUR[level-1]; UserConfigParams::m_pixel_shaders = GFX_PIXEL_SHADERS[level-1]; + UserConfigParams::m_mlaa = GFX_MLAA[level-1]; + UserConfigParams::m_ssao = GFX_SSAO[level-1]; + UserConfigParams::m_shadows = GFX_SHADOWS[level-1]; updateGfxSlider(); } @@ -473,13 +493,6 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name, assert( vsync != NULL ); UserConfigParams::m_vsync = vsync->getState(); } - else if (name == "fbos") - { - GUIEngine::CheckBoxWidget* fbos = - getWidget("fbos"); - assert( fbos != NULL ); - UserConfigParams::m_fbo = fbos->getState(); - } else if (name == "rememberWinpos") { CheckBoxWidget* rememberWinpos = getWidget("rememberWinpos"); diff --git a/src/states_screens/options_screen_video.hpp b/src/states_screens/options_screen_video.hpp index de2744358..da868d35a 100644 --- a/src/states_screens/options_screen_video.hpp +++ b/src/states_screens/options_screen_video.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index b35a8983b..f7ea23376 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, SuperTuxKart-Team, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, SuperTuxKart-Team, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -64,18 +64,9 @@ RaceGUI::RaceGUI() // Marker texture has to be power-of-two for (old) OpenGL compliance m_marker_rendered_size = 2 << ((int) ceil(1.0 + log(32.0 * scaling))); m_marker_ai_size = (int)( 14.0f * scaling); - m_marker_player_size = (int)( 16.0f * scaling); - - if (UserConfigParams::m_minimal_race_gui) - { - m_map_width = (int)(160.0f * scaling); - m_map_height = (int)(160.0f * scaling); - } - else - { - m_map_width = (int)(100.0f * scaling); - m_map_height = (int)(100.0f * scaling); - } + m_marker_player_size = (int)( 16.0f * scaling); + m_map_width = (int)(100.0f * scaling); + m_map_height = (int)(100.0f * scaling); m_map_left = (int)( 10.0f * scaling); m_map_bottom = (int)( 10.0f * scaling); @@ -189,13 +180,8 @@ void RaceGUI::renderGlobal(float dt) drawGlobalMiniMap(); - if (!m_is_tutorial && - (UserConfigParams::m_minimal_race_gui == false || world->getIdent() == IDENT_STRIKES)) - { - drawGlobalPlayerIcons(m_map_height); - } - - if(world->getTrack()->isSoccer()) drawScores(); + if (!m_is_tutorial) drawGlobalPlayerIcons(m_map_height); + if(world->getTrack()->isSoccer()) drawScores(); } // renderGlobal //----------------------------------------------------------------------------- @@ -221,9 +207,7 @@ void RaceGUI::renderPlayerView(const Camera *camera, float dt) if(!World::getWorld()->isRacePhase()) return; drawPowerupIcons (kart, viewport, scaling); - - if (UserConfigParams::m_minimal_race_gui == false) - drawSpeedAndEnergy (kart, viewport, scaling); + drawSpeedAndEnergy (kart, viewport, scaling); if (!m_is_tutorial) drawRankLap (kart, viewport); diff --git a/src/states_screens/race_gui.hpp b/src/states_screens/race_gui.hpp index a159ded1e..12142b1eb 100644 --- a/src/states_screens/race_gui.hpp +++ b/src/states_screens/race_gui.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, SuperTuxKart-Team, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, SuperTuxKart-Team, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -105,7 +105,7 @@ private: /** Display items that are shown once only (for all karts). */ void drawGlobalMiniMap (); void drawGlobalTimer (); - void drawScores(); + void drawScores(); public: diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 4124878d6..846024fe7 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, SuperTuxKart-Team, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, SuperTuxKart-Team, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -20,21 +20,8 @@ #include "states_screens/race_gui_base.hpp" -#ifdef __APPLE__ -# include -#else -# define _WINSOCKAPI_ -# ifdef WIN32 -# include -# endif -# ifdef ANDROID -# include -# else -# include -# endif -#endif - #include "audio/music_manager.hpp" +#include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" #include "graphics/material.hpp" #include "graphics/material_manager.hpp" @@ -70,25 +57,35 @@ RaceGUIBase::RaceGUIBase() //I18N: as in "ready, set, go", shown at the beginning of the race m_string_go = _("Go!"); //I18N: Shown when a goal is scored - m_string_goal = _("GOAL!"); + m_string_goal = _("GOAL!"); // Make the two materials permanent (in case that they are not listed // in the textures/materials.xml file). m_music_icon = material_manager->getMaterial("notes.png", /*full path*/false, /*permanent*/true); + if(!m_music_icon->getTexture()) + Log::fatal("RaceGuiBase", "Can't find 'notes.png' texture, aborting."); + m_plunger_face = material_manager->getMaterial("plungerface.png", /*full path*/false, /*permanent*/true); + if(!m_plunger_face->getTexture()) + Log::fatal("RaceGuiBase", + "Can't find 'plungerface.png' texture, aborting."); + //read frame picture for icons in the mini map. m_icons_frame = material_manager->getMaterial("icons-frame.png", /*full_path*/false, /*permanent*/true); + if(!m_icons_frame->getTexture()) + Log::fatal("RaceGuiBase", + "Can't find 'icons-frame.png' texture, aborting."); + const std::string &guid = file_manager->getGUIDir(); m_gauge_full = irr_driver->getTexture(guid+"gauge_full.png" ); m_gauge_full_bright = irr_driver->getTexture(guid+"gauge_full_bright.png" ); m_gauge_empty = irr_driver->getTexture(guid+"gauge_empty.png"); m_gauge_goal = irr_driver->getTexture(guid+"gauge_goal.png" ); - m_dist_show_overlap = 2; m_icons_inertia = 2; @@ -289,7 +286,6 @@ void RaceGUIBase::drawAllMessages(const AbstractKart* kart, // Draw less important messages first, at the very bottom of the screen // unimportant messages are skipped in multiplayer, they take too much screen space if (race_manager->getNumLocalPlayers() < 2 && - UserConfigParams::m_minimal_race_gui == false && !m_ignore_unimportant_messages) { for (AllMessageType::const_iterator i = m_messages.begin(); @@ -678,14 +674,15 @@ void RaceGUIBase::drawGlobalMusicDescription() } // drawGlobalMusicDescription //----------------------------------------------------------------------------- -void RaceGUIBase::drawGlobalGoal(){ - static video::SColor color = video::SColor(255, 255, 255, 255); - core::rect pos(UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1, - UserConfigParams::m_width>>1, - UserConfigParams::m_height>>1); - gui::IGUIFont* font = GUIEngine::getTitleFont(); - font->draw(m_string_goal.c_str(), pos, color, true, true); +void RaceGUIBase::drawGlobalGoal() +{ + static video::SColor color = video::SColor(255, 255, 255, 255); + core::rect pos(UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1, + UserConfigParams::m_width>>1, + UserConfigParams::m_height>>1); + gui::IGUIFont* font = GUIEngine::getTitleFont(); + font->draw(m_string_goal.c_str(), pos, color, true, true); } // ---------------------------------------------------------------------------- /** Draws the ready-set-go message on the screen. diff --git a/src/states_screens/race_gui_base.hpp b/src/states_screens/race_gui_base.hpp index b002b1fe5..55bce3039 100644 --- a/src/states_screens/race_gui_base.hpp +++ b/src/states_screens/race_gui_base.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -198,8 +198,8 @@ protected: const core::recti &viewport, const core::vector2df &scaling); void drawGlobalMusicDescription(); - void drawGlobalReadySetGo (); - void drawGlobalGoal (); + void drawGlobalReadySetGo(); + void drawGlobalGoal(); void drawPlungerInFace(const Camera *camera, float dt); /** Instructs the base gui to ignore unimportant messages (like * item messages). diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 3e3a5f2ba..14f86308d 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, SuperTuxKart-Team, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, SuperTuxKart-Team, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/race_gui_overworld.hpp b/src/states_screens/race_gui_overworld.hpp index 039f27209..a2901b52e 100644 --- a/src/states_screens/race_gui_overworld.hpp +++ b/src/states_screens/race_gui_overworld.hpp @@ -1,7 +1,7 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 Joerg Henrichs, SuperTuxKart-Team, Steve Baker +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2006-2013 Joerg Henrichs, SuperTuxKart-Team, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index d5289786b..24602622f 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -40,7 +40,11 @@ #include "race/highscores.hpp" #include "states_screens/feature_unlocked.hpp" #include "states_screens/main_menu_screen.hpp" +#include "states_screens/networking_lobby.hpp" +#include "states_screens/network_kart_selection.hpp" +#include "states_screens/online_screen.hpp" #include "states_screens/race_setup_screen.hpp" +#include "states_screens/server_selection.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/string_utils.hpp" @@ -104,6 +108,25 @@ void RaceResultGUI::enableAllButtons() enableGPProgress(); } + // If we're in a network world, change the buttons text + if (World::getWorld()->isNetworkWorld()) + { + Log::info("This work was networked", "This is a network world."); + top->setVisible(false); + middle->setText( _("Continue.") ); + middle->setVisible(true); + middle->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + bottom->setText( _("Quit the server.") ); + bottom->setVisible(true); + if (race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) + { + middle->setVisible(false); // you have to wait the server to start again + bottom->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + } + return; + } + Log::info("This work was NOT networked", "This is NOT a network world."); + // If something was unlocked // ------------------------- int n = unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size(); @@ -223,6 +246,30 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, assert(false); } + // If we're playing online : + if (World::getWorld()->isNetworkWorld()) + { + StateManager::get()->popMenu(); + if (name == "middle") // Continue button (return to server lobby) + { + race_manager->exitRace(); + race_manager->setAIKartOverride(""); + Screen* newStack[] = {MainMenuScreen::getInstance(), + OnlineScreen::getInstance(), + ServerSelection::getInstance(), + NetworkingLobby::getInstance(), + NULL}; + StateManager::get()->resetAndSetStack( newStack ); + } + if (name == "bottom") // Quit server (return to main menu) + { + race_manager->exitRace(); + race_manager->setAIKartOverride(""); + StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance()); + } + return; + } + // Next check for GP // ----------------- if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX) @@ -254,6 +301,7 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, { race_manager->exitRace(); race_manager->setAIKartOverride(""); + NetworkKartSelectionScreen::getInstance()->tearDown(); // be sure to delete the kart selection screen Screen* newStack[] = {MainMenuScreen::getInstance(), RaceSetupScreen::getInstance(), NULL}; @@ -267,6 +315,7 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, { race_manager->exitRace(); race_manager->setAIKartOverride(""); + NetworkKartSelectionScreen::getInstance()->tearDown(); // be sure to delete the kart selection screen StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance()); if (race_manager->raceWasStartedFromOverworld()) diff --git a/src/states_screens/race_result_gui.hpp b/src/states_screens/race_result_gui.hpp index 902a7380c..0a75fdef1 100644 --- a/src/states_screens/race_result_gui.hpp +++ b/src/states_screens/race_result_gui.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/race_setup_screen.cpp b/src/states_screens/race_setup_screen.cpp index 731d506b1..fe2583907 100644 --- a/src/states_screens/race_setup_screen.cpp +++ b/src/states_screens/race_setup_screen.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -23,6 +23,7 @@ #include "io/file_manager.hpp" #include "race/race_manager.hpp" #include "states_screens/arenas_screen.hpp" +#include "states_screens/easter_egg_screen.hpp" #include "states_screens/soccer_setup_screen.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/tracks_screen.hpp" @@ -121,7 +122,7 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, con 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() ); + StateManager::get()->pushScreen( EasterEggScreen::getInstance() ); } else if (selectedMode == IDENT_SOCCER) { @@ -149,7 +150,7 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, con { StateManager::get()->escapePressed(); } - + } // eventCallback // ----------------------------------------------------------------------------- @@ -236,7 +237,7 @@ void RaceSetupScreen::init() { w->setSelection( UserConfigParams::m_difficulty, PLAYER_ID_GAME_MASTER ); } - + SpinnerWidget* kartamount = getWidget("aikartamount"); kartamount->setActivated(); @@ -305,7 +306,7 @@ if (race_manager->getNumLocalPlayers() > 1 || UserConfigParams::m_artist_debug_m 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"); + name1 += _("Explore tracks to find all hidden eggs"); w2->addItem( name1, IDENT_EASTER, RaceManager::getIconOf(RaceManager::MINOR_MODE_EASTER_EGG)); @@ -339,13 +340,13 @@ if (race_manager->getNumLocalPlayers() > 1 || UserConfigParams::m_artist_debug_m m_mode_listener = new GameModeRibbonListener(this); w2->registerHoverListener(m_mode_listener); - - + + if (unlock_manager->getCurrentSlot()->isLocked("difficulty_best")) { RibbonWidget* w = getWidget("difficulty"); assert(w != NULL); - + int index = w->findItemNamed("best"); Widget* hardestWidget = &w->getChildren()[index]; hardestWidget->setBadge(LOCKED_BADGE); diff --git a/src/states_screens/race_setup_screen.hpp b/src/states_screens/race_setup_screen.hpp index 4b35e428e..95e61e914 100644 --- a/src/states_screens/race_setup_screen.hpp +++ b/src/states_screens/race_setup_screen.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/server_selection.cpp b/src/states_screens/server_selection.cpp new file mode 100644 index 000000000..11ce8d06a --- /dev/null +++ b/src/states_screens/server_selection.cpp @@ -0,0 +1,218 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2010 Lucas Baudin, 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 "states_screens/server_selection.hpp" + +#include +#include + +#include "guiengine/modaldialog.hpp" +#include "states_screens/dialogs/message_dialog.hpp" +#include "states_screens/dialogs/server_info_dialog.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" +#include "utils/string_utils.hpp" +#include "online/messages.hpp" +#include "audio/sfx_manager.hpp" + +using namespace Online; + +DEFINE_SCREEN_SINGLETON( ServerSelection ); + +// ---------------------------------------------------------------------------- + +ServerSelection::ServerSelection() : Screen("online/server_selection.stkgui") +{ + m_selected_index = -1; + m_refresh_request = NULL; + +} // ServerSelection + +// ---------------------------------------------------------------------------- + +ServerSelection::~ServerSelection() +{ +} // ServerSelection + +// ---------------------------------------------------------------------------- + +void ServerSelection::tearDown() +{ + delete m_refresh_request; +} // tearDown + + +// ---------------------------------------------------------------------------- + +void ServerSelection::refresh() +{ + m_refresh_request = ServersManager::get()->refreshRequest(); + m_fake_refresh = (m_refresh_request == NULL ? true : false); + m_server_list_widget->clear(); + m_server_list_widget->addItem("spacer", L""); + m_server_list_widget->addItem("loading", Messages::fetchingServers()); + m_reload_widget->setDeactivated(); +} + + +// ---------------------------------------------------------------------------- + +void ServerSelection::loadedFromFile() +{ + m_back_widget = getWidget("back"); + assert(m_back_widget != NULL); + m_reload_widget = getWidget("reload"); + assert(m_reload_widget != NULL); + m_server_list_widget = getWidget("server_list"); + assert(m_server_list_widget != NULL); + m_server_list_widget->setColumnListener(this); +} // loadedFromFile + + +// ---------------------------------------------------------------------------- + +void ServerSelection::beforeAddingWidget() +{ + m_server_list_widget->clearColumns(); + m_server_list_widget->addColumn( _("Name"), 3 ); + m_server_list_widget->addColumn( _("Players"), 1); +} +// ---------------------------------------------------------------------------- + +void ServerSelection::init() +{ + Screen::init(); + m_sort_desc = true; + + + // Set the default sort order + Server::setSortOrder(Server::SO_NAME); + refresh(); +} // init + +// ---------------------------------------------------------------------------- +/** Loads the list of all servers. The gui element will be + * updated. + * \param type Must be 'kart' or 'track'. + */ +void ServerSelection::loadList() +{ + m_server_list_widget->clear(); + ServersManager * manager = ServersManager::get(); + manager->sort(m_sort_desc); + for(int i=0; i < manager->getNumServers(); i++) + { + const Server * server = manager->getServerBySort(i); + core::stringw num_players; + num_players.append(StringUtils::toWString(server->getCurrentPlayers())); + num_players.append("/"); + num_players.append(StringUtils::toWString(server->getMaxPlayers())); + PtrVector * row = new PtrVector; + row->push_back(new GUIEngine::ListWidget::ListCell(server->getName(),-1,3)); + row->push_back(new GUIEngine::ListWidget::ListCell(num_players,-1,1,true)); + m_server_list_widget->addItem("server", row); + } +} // loadList + +// ---------------------------------------------------------------------------- +void ServerSelection::onColumnClicked(int column_id) +{ + switch(column_id) + { + case 0: Server::setSortOrder(Server::SO_NAME); break; + case 1: Server::setSortOrder(Server::SO_PLAYERS); break; + default: assert(0); break; + } // switch + /** \brief Toggle the sort order after column click **/ + m_sort_desc = !m_sort_desc; + loadList(); +} // onColumnClicked + +// ---------------------------------------------------------------------------- +void ServerSelection::eventCallback( GUIEngine::Widget* widget, + const std::string& name, + const int playerID) +{ + if (name == "back") + { + StateManager::get()->escapePressed(); + } + + else if (name == "reload") + { + refresh(); + } + + else if (name == m_server_list_widget->m_properties[GUIEngine::PROP_ID]) + { + m_selected_index = m_server_list_widget->getSelectionID(); + uint32_t server_id = ServersManager::get()->getServerBySort(m_selected_index)->getServerId(); + uint32_t host_id = ServersManager::get()->getServerBySort(m_selected_index)->getHostId(); + new ServerInfoDialog(server_id, host_id); + } + +} // eventCallback + +// ---------------------------------------------------------------------------- +/** Selects the last selected item on the list (which is the item that + * is just being installed) again. This function is used from the + * addons_loading screen: when it is closed, it will reset the + * select item so that people can keep on installing from that + * point on. +*/ +void ServerSelection::setLastSelected() +{ + if(m_selected_index>-1) + { + m_server_list_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + m_server_list_widget->setSelectionID(m_selected_index); + } +} // setLastSelected + +// ---------------------------------------------------------------------------- + +void ServerSelection::onUpdate(float dt, irr::video::IVideoDriver*) +{ + if(m_refresh_request != NULL) + { + if(m_refresh_request->isDone()) + { + if(m_refresh_request->isSuccess()) + { + loadList(); + } + else + { + sfx_manager->quickSound( "anvil" ); + new MessageDialog(m_refresh_request->getInfo()); + } + delete m_refresh_request; + m_refresh_request = NULL; + m_reload_widget->setActivated(); + } + else + { + m_server_list_widget->renameItem("loading", Messages::fetchingServers()); + } + } + else if(m_fake_refresh) + { + loadList(); + m_fake_refresh = false; + m_reload_widget->setActivated(); + } +} // onUpdate diff --git a/src/states_screens/server_selection.hpp b/src/states_screens/server_selection.hpp new file mode 100644 index 000000000..724f48792 --- /dev/null +++ b/src/states_screens/server_selection.hpp @@ -0,0 +1,86 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_SERVER_SELECTION_HPP +#define HEADER_SERVER_SELECTION_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "online/servers_manager.hpp" + +namespace GUIEngine { class Widget; } + +/** + * \brief ServerSelection + * \ingroup states_screens + */ +class ServerSelection : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton, + public GUIEngine::IListWidgetHeaderListener +{ + friend class GUIEngine::ScreenSingleton; + +private: + ServerSelection(); + ~ServerSelection(); + + GUIEngine::IconButtonWidget * m_back_widget; + GUIEngine::IconButtonWidget * m_reload_widget; + GUIEngine::LabelWidget * m_update_status; + GUIEngine::ListWidget * m_server_list_widget; + + + /** The currently selected index, used to re-select this item after + * addons_loading is being displayed. */ + int m_selected_index; + + /** \brief To check (and set) if sort order is descending **/ + bool m_sort_desc; + + const Online::ServersManager::RefreshRequest * m_refresh_request; + bool m_fake_refresh; + void refresh(); + +public: + + /** Load the addons into the main list.*/ + void loadList(); + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + virtual void onColumnClicked(int columnId); + + virtual void init() OVERRIDE; + + virtual void tearDown() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void onUpdate(float dt, irr::video::IVideoDriver*) OVERRIDE; + + void setLastSelected(); + +}; + +#endif diff --git a/src/states_screens/soccer_setup_screen.cpp b/src/states_screens/soccer_setup_screen.cpp index 2e3ae4cee..6c50af361 100644 --- a/src/states_screens/soccer_setup_screen.cpp +++ b/src/states_screens/soccer_setup_screen.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 Lionel Fuentes +// Copyright (C) 2013-2013 Lionel Fuentes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -23,6 +23,7 @@ #include "states_screens/arenas_screen.hpp" #include "guiengine/widgets/button_widget.hpp" #include "guiengine/widgets/spinner_widget.hpp" +#include "guiengine/widgets/check_box_widget.hpp" #include "guiengine/widgets/label_widget.hpp" #include "guiengine/widgets/model_view_widget.hpp" #include "guiengine/scalable_font.hpp" @@ -56,13 +57,31 @@ void SoccerSetupScreen::eventCallback(Widget* widget, const std::string& name, c if(name == "continue") { StateManager::get()->pushScreen( ArenasScreen::getInstance() ); - race_manager->setMaxGoal(getWidget("goalamount")->getValue()); + if(getWidget("goalamount")->isActivated()) + race_manager->setMaxGoal(getWidget("goalamount")->getValue()); + else + race_manager->setTimeTarget((float)getWidget("timeamount")->getValue()*60); + input_manager->setMasterPlayerOnly(true); } else if (name == "back") { StateManager::get()->escapePressed(); } + else if(name == "time_enabled") + { + CheckBoxWidget* timeEnabled = dynamic_cast(widget); + if(timeEnabled->getState()) + { + getWidget("goalamount")->setDeactivated(); + getWidget("timeamount")->setActivated(); + } + else + { + getWidget("timeamount")->setDeactivated(); + getWidget("goalamount")->setActivated(); + } + } } // ----------------------------------------------------------------------------- @@ -141,6 +160,13 @@ void SoccerSetupScreen::init() // TODO: remember in config.xml the last number of goals SpinnerWidget* goalamount = getWidget("goalamount"); goalamount->setValue(3); + goalamount->setDeactivated(); + goalamount->setDeactivated(); + + SpinnerWidget* timeAmount = getWidget("timeamount"); + timeAmount->setValue(timeAmount->getMin()); + + CheckBoxWidget* timeEnabled = getWidget("time_enabled"); // Set focus on "continue" ButtonWidget* bt_continue = getWidget("continue"); diff --git a/src/states_screens/soccer_setup_screen.hpp b/src/states_screens/soccer_setup_screen.hpp index aa34e0f62..ae317f943 100644 --- a/src/states_screens/soccer_setup_screen.hpp +++ b/src/states_screens/soccer_setup_screen.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 Lionel Fuentes +// Copyright (C) 2013-2013 Lionel Fuentes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/state_manager.cpp b/src/states_screens/state_manager.cpp index 74a1a9c72..5d5de01dd 100644 --- a/src/states_screens/state_manager.cpp +++ b/src/states_screens/state_manager.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -31,6 +31,7 @@ #include "modes/profile_world.hpp" #include "modes/world.hpp" #include "utils/translation.hpp" +#include "utils/log.hpp" using namespace GUIEngine; @@ -68,7 +69,7 @@ StateManager::ActivePlayer* StateManager::getActivePlayer(const int id) } else { - fprintf(stderr, "getActivePlayer(): id out of bounds\n"); + Log::error("StateManager", "getActivePlayer(): id %d out of bounds", id); assert(false); return NULL; } @@ -100,11 +101,11 @@ void StateManager::updateActivePlayerIDs() // ---------------------------------------------------------------------------- -int StateManager::createActivePlayer(PlayerProfile *profile, InputDevice *device) +int StateManager::createActivePlayer(PlayerProfile *profile, InputDevice *device, Online::Profile* user) { ActivePlayer *p; int i; - p = new ActivePlayer(profile, device); + p = new ActivePlayer(profile, device, user); i = m_active_players.size(); m_active_players.push_back(p); @@ -167,7 +168,8 @@ void StateManager::escapePressed() // when another modal dialog is visible else if(ModalDialog::isADialogActive()) { - ModalDialog::getCurrent()->escapePressed(); + if(ModalDialog::getCurrent()->onEscapePressed()) + ModalDialog::getCurrent()->dismiss(); } // In-game else if(m_game_mode == GAME) @@ -202,7 +204,7 @@ void StateManager::onGameStateChange(GameState new_state) if (new_state == MENU) { - Screen* screen = GUIEngine::getCurrentScreen(); + GUIEngine::Screen* screen = GUIEngine::getCurrentScreen(); if (screen != NULL) { music_manager->startMusic( @@ -250,7 +252,8 @@ void StateManager::onStackEmptied() #endif StateManager::ActivePlayer::ActivePlayer(PlayerProfile* player, - InputDevice *device) + InputDevice *device, + Online::Profile* user) { #ifdef DEBUG m_magic_number = 0xAC1EF1AE; @@ -259,6 +262,7 @@ StateManager::ActivePlayer::ActivePlayer(PlayerProfile* player, m_player = player; m_device = NULL; m_kart = NULL; + m_online_user = user; setDevice(device); } // ActivePlayer diff --git a/src/states_screens/state_manager.hpp b/src/states_screens/state_manager.hpp index 7db8c6798..b101da7a6 100644 --- a/src/states_screens/state_manager.hpp +++ b/src/states_screens/state_manager.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,6 +34,10 @@ class AbstractKart; class InputDevice; struct Input; +namespace Online +{ + class Profile; +} namespace GUIEngine { @@ -72,6 +76,7 @@ public: { friend class StateManager; + Online::Profile *m_online_user; PlayerProfile *m_player; InputDevice *m_device; @@ -81,7 +86,7 @@ public: /** ID of this player within the list of active players */ int m_id; - ActivePlayer(PlayerProfile* player, InputDevice* device); + ActivePlayer(PlayerProfile* player, InputDevice* device, Online::Profile* user); #ifdef DEBUG unsigned int m_magic_number; @@ -102,9 +107,9 @@ public: PlayerProfile* getProfile() { #ifdef DEBUG - assert(m_magic_number == 0xAC1EF1AE); + assert(m_magic_number == 0xAC1EF1AE); #endif - return m_player; + return m_player; } // getProfile // -------------------------------------------------------------------- @@ -112,9 +117,9 @@ public: const PlayerProfile* getConstProfile() const { #ifdef DEBUG - assert(m_magic_number == 0xAC1EF1AE); + assert(m_magic_number == 0xAC1EF1AE); #endif - return m_player; + return m_player; } // getConstProfile // -------------------------------------------------------------------- @@ -122,14 +127,24 @@ public: * selecting his identity) */ void setPlayerProfile(PlayerProfile* player); + // -------------------------------------------------------------------- + Online::Profile* getOnlineUser() + { + return m_online_user; + } + // -------------------------------------------------------------------- + /** Call to change the identity of this player (useful when player is + * selecting his identity) */ + void setOnlineUser(Online::Profile* user) { m_online_user = user; } + // -------------------------------------------------------------------- /** ID of this player within the list of active players */ int getID() const { #ifdef DEBUG - assert(m_magic_number == 0xAC1EF1AE); + assert(m_magic_number == 0xAC1EF1AE); #endif - return m_id; + return m_id; } // getID // -------------------------------------------------------------------- @@ -138,9 +153,9 @@ public: InputDevice* getDevice() const { #ifdef DEBUG - assert(m_magic_number == 0xAC1EF1AE); + assert(m_magic_number == 0xAC1EF1AE); #endif - return m_device; + return m_device; } // getDevice // -------------------------------------------------------------------- @@ -151,9 +166,9 @@ public: void setKart(AbstractKart *kart) { #ifdef DEBUG - assert(m_magic_number == 0xAC1EF1AE); + assert(m_magic_number == 0xAC1EF1AE); #endif - m_kart = kart; + m_kart = kart; } // setKart // -------------------------------------------------------------------- @@ -161,7 +176,7 @@ public: AbstractKart* getKart() { #ifdef DEBUG - assert(m_magic_number == 0xAC1EF1AE); + assert(m_magic_number == 0xAC1EF1AE); #endif return m_kart; } // getKart @@ -178,7 +193,7 @@ public: */ const PlayerProfile* getActivePlayerProfile(const int id); - int createActivePlayer(PlayerProfile *profile, InputDevice *device); + int createActivePlayer(PlayerProfile *profile, InputDevice *device, Online::Profile* use); void removeActivePlayer(int id); int activePlayerCount(); diff --git a/src/states_screens/story_mode_lobby.cpp b/src/states_screens/story_mode_lobby.cpp index 1345d51b4..64376d370 100644 --- a/src/states_screens/story_mode_lobby.cpp +++ b/src/states_screens/story_mode_lobby.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/story_mode_lobby.hpp b/src/states_screens/story_mode_lobby.hpp index 756273137..a38b83c24 100644 --- a/src/states_screens/story_mode_lobby.hpp +++ b/src/states_screens/story_mode_lobby.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 31aa297e8..1eca14476 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -85,7 +85,10 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, const if (clickedTrack != NULL) { - ITexture* screenshot = irr_driver->getTexture( clickedTrack->getScreenshotFile().c_str() ); + ITexture* screenshot = + irr_driver->getTexture( clickedTrack->getScreenshotFile(), + "While loading screenshot for track '%s':", + clickedTrack->getFilename() ); new TrackInfoDialog(selection, clickedTrack->getIdent(), translations->fribidize(clickedTrack->getName()), @@ -105,7 +108,10 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, const Track* clickedTrack = track_manager->getTrack(selection); if (clickedTrack != NULL) { - ITexture* screenshot = irr_driver->getTexture( clickedTrack->getScreenshotFile().c_str() ); + ITexture* screenshot = + irr_driver->getTexture( clickedTrack->getScreenshotFile(), + "While loading screenshot for track '%s'", + clickedTrack->getFilename()); new TrackInfoDialog(selection, clickedTrack->getIdent(), translations->fribidize(clickedTrack->getName()), @@ -252,14 +258,16 @@ void TracksScreen::init() buildTrackList(); - // select something for the game master - // FIXME: 'setSelection' will not scroll up to the passed track, so if given track - // is not visible with current scrolling this fails + // select old track for the game master (if found) + irr_driver->setTextureErrorMessage( + "While loading screenshot in track screen for last track '%s':", + UserConfigParams::m_last_track); if (!tracks_widget->setSelection(UserConfigParams::m_last_track, PLAYER_ID_GAME_MASTER, true)) { tracks_widget->setSelection(0, PLAYER_ID_GAME_MASTER, true); } + irr_driver->unsetTextureErrorMessage(); } // ----------------------------------------------------------------------------- diff --git a/src/states_screens/tracks_screen.hpp b/src/states_screens/tracks_screen.hpp index e3276874f..49d651a4c 100644 --- a/src/states_screens/tracks_screen.hpp +++ b/src/states_screens/tracks_screen.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Marianne Gagnon +// Copyright (C) 2009-2013 Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/dictionary.cpp b/src/tinygettext/dictionary.cpp index 4829e974b..4873527eb 100644 --- a/src/tinygettext/dictionary.cpp +++ b/src/tinygettext/dictionary.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/dictionary.hpp b/src/tinygettext/dictionary.hpp index f902d2821..dd492c17c 100644 --- a/src/tinygettext/dictionary.hpp +++ b/src/tinygettext/dictionary.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/dictionary_manager.cpp b/src/tinygettext/dictionary_manager.cpp index bef88d573..8c2e13aaa 100644 --- a/src/tinygettext/dictionary_manager.cpp +++ b/src/tinygettext/dictionary_manager.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/dictionary_manager.hpp b/src/tinygettext/dictionary_manager.hpp index bd1de629a..ecd9879ea 100644 --- a/src/tinygettext/dictionary_manager.hpp +++ b/src/tinygettext/dictionary_manager.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/file_system.hpp b/src/tinygettext/file_system.hpp index aff3bfd8d..9881637b5 100644 --- a/src/tinygettext/file_system.hpp +++ b/src/tinygettext/file_system.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/iconv.cpp b/src/tinygettext/iconv.cpp index 1a9c2873a..da4d5e400 100644 --- a/src/tinygettext/iconv.cpp +++ b/src/tinygettext/iconv.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/iconv.hpp b/src/tinygettext/iconv.hpp index 2b43ef5b8..e1e43122c 100644 --- a/src/tinygettext/iconv.hpp +++ b/src/tinygettext/iconv.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // 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 diff --git a/src/tinygettext/language.cpp b/src/tinygettext/language.cpp index c3888c5bc..daca81f31 100644 --- a/src/tinygettext/language.cpp +++ b/src/tinygettext/language.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/language.hpp b/src/tinygettext/language.hpp index 0ab2f3291..037200f59 100644 --- a/src/tinygettext/language.hpp +++ b/src/tinygettext/language.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/log_stream.hpp b/src/tinygettext/log_stream.hpp index 2d4bb9053..333c72ae5 100644 --- a/src/tinygettext/log_stream.hpp +++ b/src/tinygettext/log_stream.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/plural_forms.cpp b/src/tinygettext/plural_forms.cpp index 2f69b6118..58b9d7309 100644 --- a/src/tinygettext/plural_forms.cpp +++ b/src/tinygettext/plural_forms.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/plural_forms.hpp b/src/tinygettext/plural_forms.hpp index 2c05e6a28..b51f73aa2 100644 --- a/src/tinygettext/plural_forms.hpp +++ b/src/tinygettext/plural_forms.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/po_parser.cpp b/src/tinygettext/po_parser.cpp index fbe0293be..2a6bbb3c0 100644 --- a/src/tinygettext/po_parser.cpp +++ b/src/tinygettext/po_parser.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -382,7 +382,7 @@ POParser::parse() { std::string msgid_plural = get_string(12); std::vector msgstr_num; - bool saw_nonempty_msgstr = false; + bool saw_nonempty_msgstr = false; next: if (is_empty_line()) @@ -395,10 +395,10 @@ POParser::parse() isdigit(current_line[7]) && current_line[8] == ']') { unsigned int number = static_cast(current_line[7] - '0'); - std::string msgstr = get_string(9); + std::string msgstr = get_string(9); - if(!msgstr.empty()) - saw_nonempty_msgstr = true; + if(!msgstr.empty()) + saw_nonempty_msgstr = true; if (number >= msgstr_num.size()) msgstr_num.resize(number+1); @@ -414,38 +414,38 @@ POParser::parse() if (!is_empty_line()) error("expected 'msgstr[N]' or empty line"); - if (saw_nonempty_msgstr) - { - if (use_fuzzy || !fuzzy) + if (saw_nonempty_msgstr) + { + if (use_fuzzy || !fuzzy) { - if (!dict.get_plural_forms()) - { - warning("msgstr[N] seen, but no Plural-Forms given"); - } - else - { - if (msgstr_num.size() != dict.get_plural_forms().get_nplural()) - { - warning("msgstr[N] count doesn't match Plural-Forms.nplural"); - } - } + if (!dict.get_plural_forms()) + { + warning("msgstr[N] seen, but no Plural-Forms given"); + } + else + { + if (msgstr_num.size() != dict.get_plural_forms().get_nplural()) + { + warning("msgstr[N] count doesn't match Plural-Forms.nplural"); + } + } - if (has_msgctxt) - dict.add_translation(msgctxt, msgid, msgid_plural, msgstr_num); - else - dict.add_translation(msgid, msgid_plural, msgstr_num); - } + if (has_msgctxt) + dict.add_translation(msgctxt, msgid, msgid_plural, msgstr_num); + else + dict.add_translation(msgid, msgid_plural, msgstr_num); + } - if (0) - { - std::cout << (fuzzy?"fuzzy":"not-fuzzy") << std::endl; - std::cout << "msgid \"" << msgid << "\"" << std::endl; - std::cout << "msgid_plural \"" << msgid_plural << "\"" << std::endl; - for(std::vector::size_type i = 0; i < msgstr_num.size(); ++i) - std::cout << "msgstr[" << i << "] \"" << msgstr_num[i] /*conv.convert(msgstr_num[i])*/ << "\"" << std::endl; - std::cout << std::endl; - } - } + if (0) + { + std::cout << (fuzzy?"fuzzy":"not-fuzzy") << std::endl; + std::cout << "msgid \"" << msgid << "\"" << std::endl; + std::cout << "msgid_plural \"" << msgid_plural << "\"" << std::endl; + for(std::vector::size_type i = 0; i < msgstr_num.size(); ++i) + std::cout << "msgstr[" << i << "] \"" << msgstr_num[i] /*conv.convert(msgstr_num[i])*/ << "\"" << std::endl; + std::cout << std::endl; + } + } } else if (prefix("msgstr")) { diff --git a/src/tinygettext/po_parser.hpp b/src/tinygettext/po_parser.hpp index 02bc5b42a..c02949e12 100644 --- a/src/tinygettext/po_parser.hpp +++ b/src/tinygettext/po_parser.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/stk_file_system.cpp b/src/tinygettext/stk_file_system.cpp index fc51042f3..26bdc8d35 100644 --- a/src/tinygettext/stk_file_system.cpp +++ b/src/tinygettext/stk_file_system.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/stk_file_system.hpp b/src/tinygettext/stk_file_system.hpp index e120d5b69..272201c20 100644 --- a/src/tinygettext/stk_file_system.hpp +++ b/src/tinygettext/stk_file_system.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/tgt_log.cpp b/src/tinygettext/tgt_log.cpp index 349bb37d0..3a9bc900e 100644 --- a/src/tinygettext/tgt_log.cpp +++ b/src/tinygettext/tgt_log.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/tgt_log.hpp b/src/tinygettext/tgt_log.hpp index 4adc80fd5..393dbb748 100644 --- a/src/tinygettext/tgt_log.hpp +++ b/src/tinygettext/tgt_log.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2009 Ingo Ruhnke +// Copyright (C) 2009-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/tinygettext.cpp b/src/tinygettext/tinygettext.cpp index 7f5adc8ae..f0134fc24 100644 --- a/src/tinygettext/tinygettext.cpp +++ b/src/tinygettext/tinygettext.cpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tinygettext/tinygettext.hpp b/src/tinygettext/tinygettext.hpp index 19fd4c657..1924299db 100644 --- a/src/tinygettext/tinygettext.hpp +++ b/src/tinygettext/tinygettext.hpp @@ -1,5 +1,5 @@ // tinygettext - A gettext replacement that works directly on .po files -// Copyright (C) 2006 Ingo Ruhnke +// Copyright (C) 2006-2013 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/ambient_light_sphere.cpp b/src/tracks/ambient_light_sphere.cpp index e0e2fa9cc..3b1bdf9b9 100644 --- a/src/tracks/ambient_light_sphere.cpp +++ b/src/tracks/ambient_light_sphere.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/ambient_light_sphere.hpp b/src/tracks/ambient_light_sphere.hpp index 5cc494f77..74ae319c8 100644 --- a/src/tracks/ambient_light_sphere.hpp +++ b/src/tracks/ambient_light_sphere.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/bezier_curve.cpp b/src/tracks/bezier_curve.cpp index 338c8bca1..8d45c7676 100644 --- a/src/tracks/bezier_curve.cpp +++ b/src/tracks/bezier_curve.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/bezier_curve.hpp b/src/tracks/bezier_curve.hpp index d9bce843d..125940254 100644 --- a/src/tracks/bezier_curve.hpp +++ b/src/tracks/bezier_curve.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 9a55abcbd..5f84d0615 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index 3c14ed2c1..9e3a62dea 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -36,7 +36,7 @@ class XMLNode; class CheckCannon : public CheckLine { private: - /** The target point the kart will fly to. */ + /** The target point the kart will fly to. */ core::line3df m_target; /** Stores the cannon curve data. */ diff --git a/src/tracks/check_goal.cpp b/src/tracks/check_goal.cpp index 1213bc3c5..36b3edbcf 100644 --- a/src/tracks/check_goal.cpp +++ b/src/tracks/check_goal.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_goal.hpp b/src/tracks/check_goal.hpp index 577a0be49..3a4b5c012 100644 --- a/src/tracks/check_goal.hpp +++ b/src/tracks/check_goal.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_lap.cpp b/src/tracks/check_lap.cpp index 5d2e771be..01865b483 100644 --- a/src/tracks/check_lap.cpp +++ b/src/tracks/check_lap.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_lap.hpp b/src/tracks/check_lap.hpp index f873928ab..0d07cb310 100644 --- a/src/tracks/check_lap.hpp +++ b/src/tracks/check_lap.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_line.cpp b/src/tracks/check_line.cpp index c5b9b5a64..f66cdfaff 100644 --- a/src/tracks/check_line.cpp +++ b/src/tracks/check_line.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_line.hpp b/src/tracks/check_line.hpp index f0c14b4d8..9b95a09eb 100644 --- a/src/tracks/check_line.hpp +++ b/src/tracks/check_line.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp index 54b0b800b..f80b6be39 100644 --- a/src/tracks/check_manager.cpp +++ b/src/tracks/check_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_manager.hpp b/src/tracks/check_manager.hpp index a861be086..70bc0f5a2 100644 --- a/src/tracks/check_manager.hpp +++ b/src/tracks/check_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_sphere.cpp b/src/tracks/check_sphere.cpp index 68b34181c..465b67bdb 100644 --- a/src/tracks/check_sphere.cpp +++ b/src/tracks/check_sphere.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_sphere.hpp b/src/tracks/check_sphere.hpp index d9befa876..4d7a5fc90 100644 --- a/src/tracks/check_sphere.hpp +++ b/src/tracks/check_sphere.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/check_structure.cpp b/src/tracks/check_structure.cpp index 0c7b7c545..be36cc64a 100644 --- a/src/tracks/check_structure.cpp +++ b/src/tracks/check_structure.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -49,8 +49,8 @@ CheckStructure::CheckStructure(const XMLNode &node, unsigned int index) else if(kind=="ambient-light") m_check_type = CT_AMBIENT_SPHERE; // Cannons don't have a kind specified, so test for the name in this case - else if(node.getName()=="cannon") - m_check_type = CT_CANNON; + else if(node.getName()=="cannon") + m_check_type = CT_CANNON; else { printf("Unknown check structure '%s' - ignored.\n", kind.c_str()); diff --git a/src/tracks/check_structure.hpp b/src/tracks/check_structure.hpp index 93285d0a4..e9da8b307 100644 --- a/src/tracks/check_structure.hpp +++ b/src/tracks/check_structure.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2010 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -64,7 +64,7 @@ public: * enabling you to count the lap again. */ enum CheckType {CT_NEW_LAP, CT_ACTIVATE, CT_TOGGLE, CT_CANNON, - CT_GOAL, CT_AMBIENT_SPHERE}; + CT_GOAL, CT_AMBIENT_SPHERE}; protected: /** Stores the previous position of all karts. This is needed to detect diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 67e8d1ef3..e43eeb988 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 482602d74..43d1610f5 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/lod_node_loader.cpp b/src/tracks/lod_node_loader.cpp index cb6f2d450..57f690150 100644 --- a/src/tracks/lod_node_loader.cpp +++ b/src/tracks/lod_node_loader.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/lod_node_loader.hpp b/src/tracks/lod_node_loader.hpp index 22f76127d..3da7b9eca 100644 --- a/src/tracks/lod_node_loader.hpp +++ b/src/tracks/lod_node_loader.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 2984512fe..7f281e396 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 80e3bc8f1..4688ecb60 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index d85b87c01..e68041a84 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,7 +24,11 @@ #include #include "config/user_config.hpp" +#include "graphics/callbacks.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/screenquad.hpp" +#include "graphics/shaders.hpp" +#include "graphics/rtts.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "modes/world.hpp" @@ -966,10 +970,12 @@ int QuadGraph::findOutOfRoadSector(const Vec3& xyz, //----------------------------------------------------------------------------- /** Takes a snapshot of the driveline quads so they can be used as minimap. */ -video::ITexture *QuadGraph::makeMiniMap(const core::dimension2du &dimension, +video::ITexture *QuadGraph::makeMiniMap(const core::dimension2du &origdimension, const std::string &name, const video::SColor &fill_color) { + const core::dimension2du dimension = origdimension * 2; + IrrDriver::RTTProvider rttProvider(dimension, name, true); video::SColor red(128, 255, 0, 0); createMesh(/*show_invisible part of the track*/ false, @@ -1047,8 +1053,28 @@ video::ITexture *QuadGraph::makeMiniMap(const core::dimension2du &dimension, { Log::error("Quad Graph", "[makeMiniMap] WARNING: RTT does not appear to work," "mini-map will not be available."); + return NULL; } + if (!irr_driver->isGLSL()) + return texture; + + GaussianBlurProvider * const gacb = (GaussianBlurProvider *) irr_driver->getCallback(ES_GAUSSIAN3H); + gacb->setResolution(UserConfigParams::m_width, UserConfigParams::m_height); + + ScreenQuad sq(irr_driver->getVideoDriver()); + sq.getMaterial().MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + sq.setTexture(texture); + + // Horizontal pass + sq.render(irr_driver->getRTT(RTT_TMP1)); + + // Vertical pass + sq.getMaterial().MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + sq.setTexture(irr_driver->getRTT(RTT_TMP1)); + + sq.render(texture); + return texture; } // makeMiniMap diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index 228bbbf76..43eaebb5d 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/quad_set.cpp b/src/tracks/quad_set.cpp index bcb5ac999..84c9cf92e 100644 --- a/src/tracks/quad_set.cpp +++ b/src/tracks/quad_set.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/quad_set.hpp b/src/tracks/quad_set.hpp index 3e33ea48a..b7aff164c 100644 --- a/src/tracks/quad_set.hpp +++ b/src/tracks/quad_set.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/terrain_info.cpp b/src/tracks/terrain_info.cpp index 1c40922d1..251371475 100644 --- a/src/tracks/terrain_info.cpp +++ b/src/tracks/terrain_info.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/terrain_info.hpp b/src/tracks/terrain_info.hpp index 670be49bf..b9f426911 100644 --- a/src/tracks/terrain_info.hpp +++ b/src/tracks/terrain_info.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2007 Joerg Henrichs +// Copyright (C) 2007-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 95d890322..c6ca3fdd1 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1,7 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker -// 2009 Joerg Henrichs, Steve Baker +// +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2009-2013 Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -113,6 +113,16 @@ Track::Track(const std::string &filename) m_is_cutscene = false; m_camera_far = 1000.0f; m_mini_map = NULL; + m_bloom = true; + m_bloom_threshold = 0.75f; + m_color_inlevel = core::vector3df(0.0,1.0, 255.0); + m_color_outlevel = core::vector2df(0.0, 255.0); + m_clouds = false; + m_lensflare = false; + m_godrays = false; + m_displacement_speed = 1.0f; + m_caustics_speed = 1.0f; + m_shadows = true; m_sky_particles = NULL; m_sky_dx = 0.05f; m_sky_dy = 0.0f; @@ -260,6 +270,12 @@ void Track::cleanup() material_manager->popTempMaterial(); } + irr_driver->clearGlowingNodes(); + irr_driver->clearLights(); + irr_driver->clearForcedBloom(); + irr_driver->clearDisplacingNodes(); + irr_driver->clearBackgroundNodes(); + if(UserConfigParams::logMemory()) { Log::debug("track", @@ -311,9 +327,11 @@ void Track::loadTrackInfo() { // Default values m_use_fog = false; - m_fog_density = 1.0f/100.0f; + m_fog_max = 1.0f; m_fog_start = 0.0f; m_fog_end = 1000.0f; + m_fog_height_start = 0.0f; + m_fog_height_end = 100.0f; m_gravity = 9.80665f; m_smooth_normals = false; /* ARGB */ @@ -349,6 +367,16 @@ void Track::loadTrackInfo() root->get("internal", &m_internal); root->get("reverse", &m_reverse_available); root->get("push-back", &m_enable_push_back); + root->get("clouds", &m_clouds); + root->get("bloom", &m_bloom); + root->get("bloom-threshold", &m_bloom_threshold); + root->get("lens-flare", &m_lensflare); + root->get("shadows", &m_shadows); + root->get("god-rays", &m_godrays); + root->get("displacement-speed", &m_displacement_speed); + root->get("caustics-speed", &m_caustics_speed); + root->get("color-level-in", &m_color_inlevel); + root->get("color-level-out", &m_color_outlevel); // Make the default for auto-rescue in battle mode and soccer mode to be false if(m_is_arena || m_is_soccer) @@ -403,6 +431,7 @@ void Track::loadTrackInfo() break; } } + delete easter; } } // loadTrackInfo @@ -522,7 +551,6 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse) } } } // loadQuadGraph - //----------------------------------------------------------------------------- /** Loads the polygon graph for battle, i.e. the definition of all polys, and the way * they are connected to each other. Input file name is hardcoded for now @@ -530,11 +558,7 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse) void Track::loadBattleGraph() { BattleGraph::create(m_root+"navmesh.xml"); -} - - - -// ----------------------------------------------------------------------------- +}// ----------------------------------------------------------------------------- void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const { QuadGraph::get()->mapPoint2MiniMap(xyz, draw_at); @@ -1181,7 +1205,7 @@ bool Track::loadMainTrack(const XMLNode &root) convertTrackToBullet(m_all_nodes[i]); } - // Now convert all objects that are only used for the physics + // Now convert all objects that are only used for the physics // (like invisible walls). for(unsigned int i=0; iaddWaterNode(mesh, + scene::IMesh *welded; + scene_node = irr_driver->addWaterNode(mesh, &welded, wave_height, wave_speed, wave_length); - // 'addWaterNode' welds the mesh so keep both the original and the welded copy mesh->grab(); irr_driver->grabAllTextures(mesh); m_all_cached_meshes.push_back(mesh); - mesh = ((scene::IMeshSceneNode*)scene_node)->getMesh(); + mesh = welded; } else { @@ -1366,6 +1390,9 @@ void Track::createWater(const XMLNode &node) */ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) { + // Use m_filename to also get the path, not only the identifier + irr_driver->setTextureErrorMessage("While loading track '%s'", + m_filename ); if(!m_reverse_available) { reverse_track = false; @@ -1484,9 +1511,11 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) node->get("sun-diffuse", &m_sun_diffuse_color); node->get("fog", &m_use_fog); node->get("fog-color", &m_fog_color); - node->get("fog-density", &m_fog_density); + node->get("fog-max", &m_fog_max); node->get("fog-start", &m_fog_start); node->get("fog-end", &m_fog_end); + node->get("fog-start-height", &m_fog_height_start); + node->get("fog-end-height", &m_fog_height_end); } loadMainTrack(*root); @@ -1542,9 +1571,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) } else if(name=="checks") { - // Easter egg hunts don't have laps. - if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_EASTER_EGG) - CheckManager::get()->load(*node); + CheckManager::get()->load(*node); } else if (name=="particle-emitter") { @@ -1568,14 +1595,21 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) video::SColor color; node->get("color", &color); + const video::SColorf colorf(color); float distance = 25.0f; node->get("distance", &distance); + if (irr_driver->isGLSL()) + { + irr_driver->addLight(pos, distance, colorf.r, colorf.g, colorf.b); + } else + { scene::ILightSceneNode* node = irr_driver->getSceneManager()->addLightSceneNode(NULL, pos, color, distance); node->setLightType(video::ELT_POINT); node->enableCastShadow(true); } + } else if(name=="weather") { std::string weather_particles; @@ -1663,14 +1697,14 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) // It's important to execute this BEFORE the code that creates the skycube, // otherwise the skycube node could be modified to have fog enabled, which // we don't want - if (m_use_fog && !UserConfigParams::m_camera_debug) + if (m_use_fog && !UserConfigParams::m_camera_debug && !irr_driver->isGLSL()) { /* NOTE: if LINEAR type, density does not matter, if EXP or EXP2, start and end do not matter */ irr_driver->getVideoDriver()->setFog(m_fog_color, video::EFT_FOG_LINEAR, m_fog_start, m_fog_end, - m_fog_density); + 1.0f); } // Enable for for all track nodes if fog is used @@ -1725,21 +1759,30 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) irr_driver->getSceneManager()->setAmbientLight(m_ambient_color); // ---- Create sun (non-ambient directional light) - m_sun = irr_driver->getSceneManager()->addLightSceneNode(NULL, - m_sun_position, - m_sun_diffuse_color); - m_sun->setLightType(video::ELT_DIRECTIONAL); + if (m_sun_position.getLengthSQ() < 0.03f) + { + m_sun_position = core::vector3df(500, 250, 250); + } + + const video::SColorf tmpf(m_sun_diffuse_color); + m_sun = irr_driver->addLight(m_sun_position, 10000.0f, tmpf.r, tmpf.g, tmpf.b, true); + + if (!irr_driver->isGLSL()) + { + scene::ILightSceneNode *sun = (scene::ILightSceneNode *) m_sun; + sun->setLightType(video::ELT_DIRECTIONAL); // The angle of the light is rather important - let the sun // point towards (0,0,0). - if(m_sun_position.getLengthSQ()==0) + if(m_sun_position.getLengthSQ() < 0.03f) // Backward compatibility: if no sun is specified, use the // old hardcoded default angle m_sun->setRotation( core::vector3df(180, 45, 45) ); else m_sun->setRotation((-m_sun_position).getHorizontalAngle()); - m_sun->getLightData().SpecularColor = m_sun_specular_color; + sun->getLightData().SpecularColor = m_sun_specular_color; + } createPhysicsModel(main_track_count); @@ -1799,6 +1842,8 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) std::string dir = StringUtils::getPath(m_filename); easter_world->readData(dir+"/easter_eggs.xml"); } + + irr_driver->unsetTextureErrorMessage(); } // loadTrackModel //----------------------------------------------------------------------------- @@ -2095,7 +2140,7 @@ bool Track::findGround(AbstractKart *kart) // Material and hit point are not needed; const Material *m; Vec3 hit_point, normal; - bool over_ground = m_track_mesh->castRay(kart->getXYZ(), to, &hit_point, + bool over_ground = m_track_mesh->castRay(kart->getXYZ(), to, &hit_point, &m, &normal); const Vec3 &xyz = kart->getXYZ(); if(!over_ground || !m) @@ -2119,7 +2164,7 @@ bool Track::findGround(AbstractKart *kart) // too long. if(xyz.getY() - hit_point.getY() > 5) { - Log::warn("physics", + Log::warn("physics", "Kart at (%f %f %f) is too high above ground at (%f %f %f)", xyz.getX(),xyz.getY(),xyz.getZ(), hit_point.getX(),hit_point.getY(),hit_point.getZ()); diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 19380069c..738910edc 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -1,5 +1,7 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2009-2013 Joerg Henrichs, Steve Baker // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -220,7 +222,7 @@ private: #endif PtrVector m_all_emitters; - scene::ILightSceneNode *m_sun; + scene::ISceneNode *m_sun; /** Used to collect the triangles for the bullet mesh. */ TriangleMesh* m_track_mesh; /** Used to collect the triangles which do not have a physical @@ -349,9 +351,11 @@ private: /** True if this track supports using smoothed normals. */ bool m_smooth_normals; - float m_fog_density; + float m_fog_max; float m_fog_start; float m_fog_end; + float m_fog_height_start; + float m_fog_height_end; core::vector3df m_sun_position; /** The current ambient color for each kart. */ video::SColor m_ambient_color; @@ -366,6 +370,24 @@ private: float m_minimap_x_scale; float m_minimap_y_scale; + bool m_clouds; + + bool m_bloom; + float m_bloom_threshold; + + bool m_lensflare; + bool m_godrays; + bool m_shadows; + + float m_displacement_speed; + float m_caustics_speed; + + /** The levels for color correction + * m_color_inlevel(black, gamma, white) + * m_color_outlevel(black, white)*/ + core::vector3df m_color_inlevel; + core::vector2df m_color_outlevel; + /** List of all bezier curves in the track - for e.g. camera, ... */ std::vector m_all_curves; @@ -534,8 +556,16 @@ public: // ------------------------------------------------------------------------ float getFogEnd() const { return m_fog_end; } // ------------------------------------------------------------------------ + float getFogStartHeight() const { return m_fog_height_start; } + // ------------------------------------------------------------------------ + float getFogEndHeight() const { return m_fog_height_end; } + // ------------------------------------------------------------------------ + float getFogMax() const { return m_fog_max; } + // ------------------------------------------------------------------------ video::SColor getFogColor() const { return m_fog_color; } // ------------------------------------------------------------------------ + video::SColor getSunColor() const { return m_sun_diffuse_color; } + // ------------------------------------------------------------------------ /** Whether this is an "internal" track. If so it won't be offered * in the track selection screen. */ bool isInternal() const { return m_internal; } @@ -557,6 +587,22 @@ public: const std::vector& getSubtitles() const { return m_subtitles; } + bool hasClouds() const { return m_clouds; } + + bool getBloom() const { return m_bloom; } + float getBloomThreshold() const { return m_bloom_threshold; } + + /** Return the color levels for color correction shader */ + core::vector3df getColorLevelIn() const { return m_color_inlevel; } + core::vector2df getColorLevelOut() const { return m_color_outlevel; } + + bool hasLensFlare() const { return m_lensflare; } + bool hasGodRays() const { return m_godrays; } + bool hasShadows() const { return m_shadows; } + + + float getDisplacementSpeed() const { return m_displacement_speed; } + float getCausticsSpeed() const { return m_caustics_speed; } }; // class Track #endif diff --git a/src/tracks/track_manager.cpp b/src/tracks/track_manager.cpp index db6cafdf6..e8863cc46 100644 --- a/src/tracks/track_manager.cpp +++ b/src/tracks/track_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/track_manager.hpp b/src/tracks/track_manager.hpp index 7b7cd59e0..3faf53921 100644 --- a/src/tracks/track_manager.hpp +++ b/src/tracks/track_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 SuperTuxKart-Team +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index 53a649854..9d21e413f 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 Joerg Henrichs, Marianne Gagnon +// Copyright (C) 2013-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -26,6 +26,7 @@ #include "items/item_manager.hpp" #include "physics/physical_object.hpp" #include "race/race_manager.hpp" +#include "utils/helpers.hpp" /** A track object: any additional object on the track. This object implements @@ -133,24 +134,59 @@ void TrackObject::init(const XMLNode &xml_node, LODNode* lod_node) } else { + scene::ISceneNode *glownode = NULL; + if (lod_node != NULL) { m_type = "lod"; m_presentation = new TrackObjectPresentationLOD(xml_node, lod_node); + + glownode = lod_node->getAllNodes()[0]; } else { m_type = "mesh"; m_presentation = new TrackObjectPresentationMesh(xml_node, m_enabled); + glownode = ((TrackObjectPresentationMesh *) m_presentation)->getNode(); } - if (m_interaction != "ghost" && m_interaction != "none") + std::string render_pass; + xml_node.get("renderpass", &render_pass); + + if (m_interaction != "ghost" && m_interaction != "none" && render_pass != "skybox") { m_rigid_body = PhysicalObject::fromXML(type == "movable", xml_node, this); } + + video::SColor glow; + if (xml_node.get("glow", &glow) && glownode) + { + float r, g, b; + r = glow.getRed() / 255.0f; + g = glow.getGreen() / 255.0f; + b = glow.getBlue() / 255.0f; + + irr_driver->addGlowingNode(glownode, r, g, b); + } + + bool forcedbloom = false; + if (xml_node.get("forcedbloom", &forcedbloom) && forcedbloom && glownode) + { + float power = 1; + xml_node.get("bloompower", &power); + power = clampf(power, 0.5f, 10); + + irr_driver->addForcedBloomNode(glownode, power); + } + + bool displacing = false; + if (xml_node.get("displacing", &displacing) && displacing && glownode) + { + irr_driver->addDisplacingNode(glownode); + } } diff --git a/src/tracks/track_object.hpp b/src/tracks/track_object.hpp index 23380c592..fceba17bf 100644 --- a/src/tracks/track_object.hpp +++ b/src/tracks/track_object.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -90,7 +90,7 @@ public: TrackObjectPresentation* presentation, bool is_dynamic, const PhysicalObject::Settings* physicsSettings); - ~TrackObject(); + virtual ~TrackObject(); virtual void update(float dt); virtual void reset(); /** To finish object constructions. Called after the track model diff --git a/src/tracks/track_object_manager.cpp b/src/tracks/track_object_manager.cpp index 5558e2de3..fa8ccf3e3 100644 --- a/src/tracks/track_object_manager.cpp +++ b/src/tracks/track_object_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/track_object_manager.hpp b/src/tracks/track_object_manager.hpp index 04fb5c30f..eba7b9300 100644 --- a/src/tracks/track_object_manager.hpp +++ b/src/tracks/track_object_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs +// Copyright (C) 2009-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index e2a27a1e7..d5f20d0f6 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -1,6 +1,6 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 Joerg Henrichs, Marianne Gagnon +// +// Copyright (C) 2013-2013 Joerg Henrichs, Marianne Gagnon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -150,6 +150,15 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node std::string model_name; xml_node.get("model", &model_name ); + m_is_in_skybox = false; + std::string render_pass; + xml_node.get("renderpass", &render_pass); + + if(render_pass == "skybox") + { + m_is_in_skybox = true; + } + std::string full_path = World::getWorld()->getTrack()->getTrackFile(model_name); @@ -225,7 +234,19 @@ void TrackObjectPresentationMesh::init(const XMLNode* xml_node, bool enabled) m_mesh->grab(); irr_driver->grabAllTextures(m_mesh); - if (animated) + if (m_is_in_skybox) + { + // Tell the driver that this mesh is a part of the background + scene::IMeshSceneNode * const node = + irr_driver->getSceneManager()->addMeshSceneNode(m_mesh); + node->grab(); + node->setParent(NULL); + + irr_driver->addBackgroundNode(node); + + m_node = node; + } + else if (animated) { scene::IAnimatedMeshSceneNode *node = irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh); @@ -576,7 +597,7 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(const xml_node.get("action", &m_action); m_action_active = true; - + if (m_action.size() == 0) { fprintf(stderr, "[TrackObject] WARNING: action-trigger has no action defined\n"); @@ -588,7 +609,7 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(const void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) { if (!m_action_active) return; - + if (m_action == "garage") { new RacePausedDialog(0.8f, 0.6f); diff --git a/src/tracks/track_object_presentation.hpp b/src/tracks/track_object_presentation.hpp index 6083a2f0f..7894748cc 100644 --- a/src/tracks/track_object_presentation.hpp +++ b/src/tracks/track_object_presentation.hpp @@ -1,3 +1,22 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2013-2013 Joerg Henrichs, Marianne Gagnon +// +// 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 HEADER_TRACK_OBJECT_PRESENTATION_HPP #define HEADER_TRACK_OBJECT_PRESENTATION_HPP @@ -146,6 +165,9 @@ private: /** True if it is a looped animation. */ bool m_is_looped; + /** True if the object is in the skybox */ + bool m_is_in_skybox; + /** Start frame of the animation to be played. */ unsigned int m_frame_start; @@ -262,7 +284,7 @@ public: virtual ~TrackObjectPresentationActionTrigger() {} virtual void onTriggerItemApproached(Item* who) OVERRIDE; - + virtual void reset() OVERRIDE { m_action_active = true; } }; diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index e7bc0ecd5..82d7cf09e 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/tracks/track_sector.hpp b/src/tracks/track_sector.hpp index d67833bfa..57a0e7c1a 100644 --- a/src/tracks/track_sector.hpp +++ b/src/tracks/track_sector.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/aligned_array.hpp b/src/utils/aligned_array.hpp index f1afffc96..3f90608b8 100644 --- a/src/utils/aligned_array.hpp +++ b/src/utils/aligned_array.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/constants.cpp b/src/utils/constants.cpp index a7940f4fd..8198c673d 100644 --- a/src/utils/constants.cpp +++ b/src/utils/constants.cpp @@ -1,6 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart // +// Copyright (C) 2012-2013 SuperTuxKart-Team +// // 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 diff --git a/src/utils/constants.hpp b/src/utils/constants.hpp index b6ff8f4da..7089f62c9 100644 --- a/src/utils/constants.hpp +++ b/src/utils/constants.hpp @@ -1,6 +1,7 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// +// Copyright (C) 2004-2013 Steve Baker +// Copyright (C) 2012-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/crash_reporting.cpp b/src/utils/crash_reporting.cpp new file mode 100644 index 000000000..09115ad77 --- /dev/null +++ b/src/utils/crash_reporting.cpp @@ -0,0 +1,330 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2013 Lionel Fuentes +// +// 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 "crash_reporting.hpp" +#include "log.hpp" +#include + +#ifdef WIN32 + // --------------------- Windows version ----------------- + #include + #include + #include + #include + #include + + typedef BOOL (__stdcall *tSymCleanup)( + _In_ HANDLE hProcess + ); + typedef PVOID (__stdcall *tSymFunctionTableAccess64)( + _In_ HANDLE hProcess, + _In_ DWORD64 AddrBase + ); + typedef BOOL (__stdcall *tSymGetLineFromAddr64)( + _In_ HANDLE hProcess, + _In_ DWORD64 qwAddr, + _Out_ PDWORD pdwDisplacement, + _Out_ PIMAGEHLP_LINE64 Line64 + ); + typedef DWORD64 (__stdcall *tSymGetModuleBase64)( + _In_ HANDLE hProcess, + _In_ DWORD64 qwAddr + ); + typedef BOOL (__stdcall *tSymGetSymFromAddr64)( + _In_ HANDLE hProcess, + _In_ DWORD64 qwAddr, + _Out_opt_ PDWORD64 pdwDisplacement, + _Inout_ PIMAGEHLP_SYMBOL64 Symbol + ); + typedef BOOL (__stdcall *tSymInitialize)( + _In_ HANDLE hProcess, + _In_opt_ PCSTR UserSearchPath, + _In_ BOOL fInvadeProcess + ); + typedef DWORD (__stdcall *tSymSetOptions)( + _In_ DWORD SymOptions + ); + typedef BOOL (__stdcall *tStackWalk64)( + _In_ DWORD MachineType, + _In_ HANDLE hProcess, + _In_ HANDLE hThread, + _Inout_ LPSTACKFRAME64 StackFrame, + _Inout_ PVOID ContextRecord, + _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress + ); + typedef DWORD (__stdcall *tUnDecorateSymbolName)( + _In_ PCSTR name, + _Out_ PSTR outputString, + _In_ DWORD maxStringLength, + _In_ DWORD flags + ); + + namespace CrashReporting + { + void getCallStackWithContext(std::string& callstack, PCONTEXT pContext); + + void winCrashHandler(PCONTEXT pContext=NULL) + { + std::string callstack; + if(pContext) + getCallStackWithContext(callstack, pContext); + else + getCallStack(callstack); + + std::string msg = "SuperTuxKart crashed!\n" + "Please hit Ctrl+C to copy to clipboard and signal the problem\n" + "to the developers on our forum: http://forum.freegamedev.net/viewforum.php?f=16\n" + "\n" + "Call stack:\n"; + msg += callstack; + MessageBoxA(NULL, msg.c_str(), "SuperTuxKart crashed :/", MB_OK); + } + + LONG WINAPI sehHandler(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo) + { + winCrashHandler(ExceptionInfo->ContextRecord); + return EXCEPTION_EXECUTE_HANDLER; + } + + void pureCallHandler() + { + winCrashHandler(); + } + + int newHandler( size_t ) + { + winCrashHandler(); + return 0; + } + + void invalidParameterHandler(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t) + { + winCrashHandler(); + } + + void signalHandler(int code) + { + winCrashHandler(); + } + + void installHandlers() + { + // ----- Per-process handlers ----- + SetUnhandledExceptionFilter(sehHandler); // Top-level SEH handler + _set_purecall_handler(pureCallHandler); // Pure virtual function calls handler + + // Catch new operator memory allocation exceptions + _set_new_mode(1); // Force malloc() to call new handler too + _set_new_handler(newHandler); + + _set_invalid_parameter_handler(invalidParameterHandler); // Catch invalid parameter exceptions. + //_set_security_error_handler(securityHandler); // Catch buffer overrun exceptions + + signal(SIGABRT, signalHandler); + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + + // ----- Per-thread handlers ----- + // TODO + } + + void getCallStackWithContext(std::string& callstack, PCONTEXT pContext) + { + HINSTANCE hImageHlpDll = LoadLibraryA("imagehlp.dll"); + if(!hImageHlpDll) + { + Log::warn("CrashReporting", "Failed to load DLL imagehlp.dll"); + callstack = "Crash reporting failed to load DLL imagehlp.dll"; + return; + } + + // Retrieve the DLL functions +#define GET_FUNC_PTR(FuncName) \ + t##FuncName _##FuncName = (t##FuncName)GetProcAddress(hImageHlpDll, #FuncName); \ + if(!_##FuncName) { \ + Log::warn("CrashReporting", "Failed to import symbol " #FuncName " from imagehlp.dll"); \ + FreeLibrary(hImageHlpDll); \ + return; \ + } + + GET_FUNC_PTR(SymCleanup ) + GET_FUNC_PTR(SymFunctionTableAccess64 ) + GET_FUNC_PTR(SymGetLineFromAddr64 ) + GET_FUNC_PTR(SymGetModuleBase64 ) + GET_FUNC_PTR(SymGetSymFromAddr64 ) + GET_FUNC_PTR(SymInitialize ) + GET_FUNC_PTR(SymSetOptions ) + GET_FUNC_PTR(StackWalk64 ) + GET_FUNC_PTR(UnDecorateSymbolName ) + +#undef GET_FUNC_PTR + + const HANDLE hProcess = GetCurrentProcess(); + const HANDLE hThread = GetCurrentThread(); + + // Initialize the symbol hander for the process + { + // Get the file path of the executable + char filepath[512]; + GetModuleFileNameA(NULL, filepath, sizeof(filepath)); + if(!filepath) + { + Log::warn("CrashReporting", "GetModuleFileNameA failed"); + FreeLibrary(hImageHlpDll); + return; + } + + // Only keep the directory + char* last_separator = strchr(filepath, '/'); + if(!last_separator) last_separator = strchr(filepath, '\\'); + if(last_separator) + last_separator[0] = '\0'; + + // Finally initialize the symbol handler. + BOOL bOk = _SymInitialize(hProcess, filepath ? filepath : NULL, TRUE); + if(!bOk) + { + Log::warn("CrashReporting", "SymInitialize() failed"); + FreeLibrary(hImageHlpDll); + return; + } + + _SymSetOptions(SYMOPT_LOAD_LINES); + } + + // Get the stack trace + { + // Initialize the IMAGEHLP_SYMBOL64 structure + const size_t MaxNameLength = 256; + IMAGEHLP_SYMBOL64* sym = (IMAGEHLP_SYMBOL64*)alloca(sizeof(IMAGEHLP_SYMBOL64) + MaxNameLength); + sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + sym->MaxNameLength = MaxNameLength; + + // Initialize the STACKFRAME structure so that it + // corresponds to the current function call + STACKFRAME64 stackframe; + memset(&stackframe, 0, sizeof(stackframe)); + stackframe.AddrPC.Offset = pContext->Eip; + stackframe.AddrPC.Mode = AddrModeFlat; + stackframe.AddrStack.Offset = pContext->Esp; + stackframe.AddrStack.Mode = AddrModeFlat; + stackframe.AddrFrame.Offset = pContext->Ebp; + stackframe.AddrFrame.Mode = AddrModeFlat; + + const DWORD machine_type = IMAGE_FILE_MACHINE_I386; + + // Walk the stack + const int max_nb_calls = 32; + for(int i=0 ; i < max_nb_calls ; i++) + { + const BOOL stackframe_ok = _StackWalk64( machine_type, + hProcess, + hThread, + &stackframe, + pContext, + NULL, + _SymFunctionTableAccess64, + _SymGetModuleBase64, + NULL); + if(stackframe_ok) + { + // Decode the symbol and add it to the call stack + DWORD64 sym_displacement; + if(_SymGetSymFromAddr64( hProcess, + stackframe.AddrPC.Offset, + &sym_displacement, + sym)) + { + IMAGEHLP_LINE64 line64; + DWORD dwDisplacement = (DWORD)sym_displacement; + if(_SymGetLineFromAddr64(hProcess, stackframe.AddrPC.Offset, &dwDisplacement, &line64)) + { + callstack += "\n "; + + // Directory + filename -> filename only + const char* filename = line64.FileName; + const char* ptr = line64.FileName; + while(*ptr) + { + if(*ptr == '\\' || *ptr == '/') + filename = ptr+1; + ptr++; + } + callstack += filename; + callstack += ":"; + callstack += sym->Name; + + char str[128]; + _itoa(line64.LineNumber, str, 10); + callstack += ":"; + callstack += str; + } + else + { + callstack += "\n "; + callstack += sym->Name; + } + } + else + callstack += "\n "; + } + else + break; // done + } + } + + FreeLibrary(hImageHlpDll); + } + + + void getCallStack(std::string& callstack) + { + // Get the current CONTEXT + // NB: this code is ONLY VALID FOR X86 (32 bit)! + CONTEXT ctx; + memset(&ctx, '\0', sizeof(ctx)); + ctx.ContextFlags = CONTEXT_FULL; + __asm call x + __asm x: pop eax // get eip (can't directly use mov) + __asm mov ctx.Eip, eax + __asm mov ctx.Ebp, ebp + __asm mov ctx.Esp, esp + + getCallStackWithContext(callstack, &ctx); + } + } // end namespace CrashReporting + +#else + // --------------------- Unix version ----------------------- + namespace CrashReporting + { + void installHandlers() + { + // TODO! + } + + void getCallStack(std::string& callstack) + { + // TODO! + } + } // end namespace CrashReporting + +#endif diff --git a/src/utils/crash_reporting.hpp b/src/utils/crash_reporting.hpp new file mode 100644 index 000000000..5f5b360bd --- /dev/null +++ b/src/utils/crash_reporting.hpp @@ -0,0 +1,31 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2013 Lionel Fuentes +// +// 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 HEADER_CRASH_REPORTING_HPP +#define HEADER_CRASH_REPORTING_HPP + +#include + +namespace CrashReporting +{ + void installHandlers(); + void getCallStack(std::string& callstack); +} + + +#endif // HEADER_CRASH_REPORTING_HPP diff --git a/src/utils/helpers.cpp b/src/utils/helpers.cpp new file mode 100644 index 000000000..76ec6283c --- /dev/null +++ b/src/utils/helpers.cpp @@ -0,0 +1,220 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/irr_driver.hpp" +#include "utils/helpers.hpp" + +#include +#include + +float clampf(float in, float low, float high) { + return in > high ? high : in < low ? low : in; +} + +float smootherstep(float e0, float e1, float x) { + x = clampf((x - e0)/(e1 - e0), 0, 1); + + return x*x*x*(x*(x*6 - 15) + 10); +} + +void savetex(ITexture *tex, const char *name) { + + using namespace core; + using namespace video; + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + + IImage * const tmp = drv->createImage(tex, position2di(0,0), tex->getSize()); + + if (!name) + { + stringc namec = tex->getName().getPath(); + namec += ".png"; + drv->writeImageToFile(tmp, namec.c_str()); + } + else + { + drv->writeImageToFile(tmp, name); + } + + tmp->drop(); +} + +float mix(float x, float y, float a) { + return x * (1 - a) + y * a; +} + +unsigned ispow(const unsigned in) { + + if (in < 2) return 0; + + return !(in & (in - 1)); +} + +unsigned npow(unsigned in) { + + if (ispow(in)) return in; + + in |= in >> 1; + in |= in >> 2; + in |= in >> 4; + in |= in >> 8; + in |= in >> 16; + + return in + 1; +} + +// Simple 8-bit hash +u8 shash8(const u8 * const data, const u16 size) { + + u32 out = data[0], i; + + for (i = 1; i < size; i++) { + out += data[i] * ((i + 1) ^ data[i - 1]); + } + + out %= 307; // prime + out %= 256; // eight bits + + return out; +} + + +/* + +Copyright (C) 2011 by Ashima Arts (Simplex noise) +Copyright (C) 2011 by Lauri Kasanen (cpu port) + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +static inline float mod289(float x) { +// return x - floorf(x * (1.0 / 289.0)) * 289.0; + return fmodf(x, 289); +} + +static inline float permute(float x) { + return mod289(((x*34.0f)+1.0f)*x); +} + +// Vectorized 2d simplex noise. +float noise2d(float v1, float v2) { + + const float C[] = { + 0.211324865405187f, + 0.366025403784439f, + -0.577350269189626f, + 0.024390243902439f }; + + // First corner + float i[2]; + i[0] = floorf(v1 + v1*C[1] + v2*C[1]); + i[1] = floorf(v2 + v1*C[1] + v2*C[1]); + + float x0[2]; + x0[0] = v1 - i[0] + i[0]*C[0] + i[1]*C[0]; + x0[1] = v2 - i[1] + i[0]*C[0] + i[1]*C[0]; + + // Other corners + float i1[2]; + if (x0[0] > x0[1]) { + i1[0] = 1; + i1[1] = 0; + } else { + i1[0] = 0; + i1[1] = 1; + } + + float x12[4]; + x12[0] = x0[0] + C[0] - i1[0]; + x12[1] = x0[1] + C[0] - i1[1]; + x12[2] = x0[0] + C[2]; + x12[3] = x0[1] + C[2]; + + // Permutations + i[0] = mod289(i[0]); + i[1] = mod289(i[1]); + + float p[3]; + p[0] = permute(permute(i[1]) + i[0]); + p[1] = permute(permute(i[1] + i1[1]) + i[0] + i1[0]); + p[2] = permute(permute(i[1] + 1) + i[0] + 1); + + float m[3]; + m[0] = std::max(0.5f - x0[0]*x0[0] - x0[1]*x0[1], 0); + m[1] = std::max(0.5f - x12[0]*x12[0] - x12[1]*x12[1], 0); + m[2] = std::max(0.5f - x12[2]*x12[2] - x12[3]*x12[3], 0); + + m[0] = m[0] * m[0] * m[0] * m[0]; + m[1] = m[1] * m[1] * m[1] * m[1]; + m[2] = m[2] * m[2] * m[2] * m[2]; + + // Gradients + float tmp; + + float x[3]; + x[0] = 2 * modff(p[0] * C[3], &tmp) - 1; + x[1] = 2 * modff(p[1] * C[3], &tmp) - 1; + x[2] = 2 * modff(p[2] * C[3], &tmp) - 1; + + float h[3]; + h[0] = fabsf(x[0]) - 0.5f; + h[1] = fabsf(x[1]) - 0.5f; + h[2] = fabsf(x[2]) - 0.5f; + + float ox[3]; + ox[0] = floorf(x[0] + 0.5f); + ox[1] = floorf(x[1] + 0.5f); + ox[2] = floorf(x[2] + 0.5f); + + float a0[3]; + a0[0] = x[0] - ox[0]; + a0[1] = x[1] - ox[1]; + a0[2] = x[2] - ox[2]; + + // Normalize + m[0] *= 1.79284291400159f - 0.85373472095314f * (a0[0]*a0[0] + h[0]*h[0]); + m[1] *= 1.79284291400159f - 0.85373472095314f * (a0[1]*a0[1] + h[1]*h[1]); + m[2] *= 1.79284291400159f - 0.85373472095314f * (a0[2]*a0[2] + h[2]*h[2]); + + // Compute final value + float g[3]; + g[0] = a0[0] * x0[0] + h[0] * x0[1]; + g[1] = a0[1] * x12[0] + h[1] * x12[1]; + g[2] = a0[2] * x12[2] + h[2] * x12[3]; + + return 130 * (m[0] * g[0] + m[1] * g[1] + m[2] * g[2]); +} diff --git a/src/network/connect_message.hpp b/src/utils/helpers.hpp similarity index 63% rename from src/network/connect_message.hpp rename to src/utils/helpers.hpp index 1875b5718..c13028666 100644 --- a/src/network/connect_message.hpp +++ b/src/utils/helpers.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 Lauri Kasanen // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,22 +16,25 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_CONNECT_MESSAGE_HPP -#define HEADER_CONNECT_MESSAGE_HPP +#ifndef HELPERS_H +#define HELPERS_H -#include +#include -#include "network/message.hpp" +using irr::video::ITexture; + +float smootherstep(float, float, float); +float clampf(float, float, float); + +float mix(float x, float y, float a); + +unsigned ispow(const unsigned in); +unsigned npow(unsigned in); + +void savetex(ITexture *tex, const char *name = NULL); + +float noise2d(float v1, float v2 = 0); + +u8 shash8(const u8 * const data, const u16 size); -class ConnectMessage : public Message -{ -private: - std::string m_id; - void setId(); -public: - ConnectMessage(); - ConnectMessage(ENetPacket* pkt); - const std::string& - getId() { return m_id; } -}; // ConnectMessage #endif diff --git a/src/utils/interpolation_array.hpp b/src/utils/interpolation_array.hpp index b018fc855..19d168427 100644 --- a/src/utils/interpolation_array.hpp +++ b/src/utils/interpolation_array.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2012 Joerg Henrichs +// Copyright (C) 2012-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/leak_check.cpp b/src/utils/leak_check.cpp index d4611c1ce..ef9519ddd 100644 --- a/src/utils/leak_check.cpp +++ b/src/utils/leak_check.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011-2012 Marianne Gagnon, Joerg Henrichs +// Copyright (C) 2011-2013 Marianne Gagnon, 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 diff --git a/src/utils/leak_check.hpp b/src/utils/leak_check.hpp index b72d0ee3a..aa1318c03 100644 --- a/src/utils/leak_check.hpp +++ b/src/utils/leak_check.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011-2012 Marianne Gagnon, Joerg Henrichs +// Copyright (C) 2011-2013 Marianne Gagnon, 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 diff --git a/src/utils/log.cpp b/src/utils/log.cpp index c86ddb44c..98e58a8e3 100644 --- a/src/utils/log.cpp +++ b/src/utils/log.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 Joerg Henrichs +// Copyright (C) 2013-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -67,9 +67,9 @@ void Log::setTerminalColor(LogLevel level) #else enum TermAttr { - TERM_RESET = 0, // "normal" mode + TERM_RESET = 0, // "normal" mode TERM_BRIGHT = 1,// more luminosity for the foreground - TERM_DIM = 2, // less luminosity for the foreground + TERM_DIM = 2, // less luminosity for the foreground }; enum TermColor diff --git a/src/utils/log.hpp b/src/utils/log.hpp index 46627cec2..20125ce98 100644 --- a/src/utils/log.hpp +++ b/src/utils/log.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 Joerg Henrichs +// Copyright (C) 2013-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/no_copy.hpp b/src/utils/no_copy.hpp index eb60eccaf..3011698eb 100644 --- a/src/utils/no_copy.hpp +++ b/src/utils/no_copy.hpp @@ -1,20 +1,21 @@ -/* -Copyright (C) 2003 Matthias Braun +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2003 Matthias-2013 Braun +// +// 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 -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 __NOCOPY_H__ #define __NOCOPY_H__ diff --git a/src/utils/profiler.cpp b/src/utils/profiler.cpp index 3b053ea96..0d5a10e28 100644 --- a/src/utils/profiler.cpp +++ b/src/utils/profiler.cpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -64,7 +64,7 @@ Profiler profiler; //----------------------------------------------------------------------------- Profiler::Profiler() { - m_thread_infos.resize(1); // TODO: monothread now, should support multithreading + m_thread_infos.resize(1); // TODO: monothread now, should support multithreading m_write_id = 0; m_time_last_sync = _getTimeMilliseconds(); m_time_between_sync = 0.0; @@ -101,7 +101,7 @@ void Profiler::popCpuMarker() if(m_freeze_state == FROZEN || m_freeze_state == WAITING_FOR_UNFREEZE) return; - ThreadInfo& ti = getThreadInfo(); + ThreadInfo& ti = getThreadInfo(); assert(ti.markers_stack[m_write_id].size() > 0); MarkerStack& markers_stack = ti.markers_stack[m_write_id]; @@ -195,7 +195,7 @@ void Profiler::draw() int read_id = !m_write_id; // Compute some values for drawing (unit: pixels, but we keep floats for reducing errors accumulation) - core::dimension2d screen_size = driver->getScreenSize(); + core::dimension2d screen_size = driver->getScreenSize(); const double profiler_width = (1.0 - 2.0*MARGIN_X) * screen_size.Width; const double x_offset = MARGIN_X*screen_size.Width; const double y_offset = (MARGIN_Y + LINE_HEIGHT)*screen_size.Height; @@ -220,12 +220,12 @@ void Profiler::draw() MarkerList::const_iterator it_end = markers.end(); for(MarkerList::const_iterator it = markers.begin() ; it != it_end ; it++) { - const Marker& m = *it; + const Marker& m = *it; assert(m.end >= 0.0); - core::rect pos((s32)( x_offset + factor*m.start ), - (s32)( y_offset + i*line_height ), - (s32)( x_offset + factor*m.end ), - (s32)( y_offset + (i+1)*line_height )); + core::rect pos((s32)( x_offset + factor*m.start ), + (s32)( y_offset + i*line_height ), + (s32)( x_offset + factor*m.end ), + (s32)( y_offset + (i+1)*line_height )); // Reduce vertically the size of the markers according to their layer pos.UpperLeftCorner.Y += m.layer; diff --git a/src/utils/profiler.hpp b/src/utils/profiler.hpp index 3434f170b..738e2e633 100644 --- a/src/utils/profiler.hpp +++ b/src/utils/profiler.hpp @@ -1,5 +1,5 @@ // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 SuperTuxKart-Team +// Copyright (C) 2004-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -77,8 +77,8 @@ private: } }; - typedef std::list MarkerList; - typedef std::stack MarkerStack; + typedef std::list MarkerList; + typedef std::stack MarkerStack; struct ThreadInfo { @@ -86,7 +86,7 @@ private: MarkerStack markers_stack[2]; }; - typedef std::vector ThreadInfoList; + typedef std::vector ThreadInfoList; ThreadInfoList m_thread_infos; int m_write_id; diff --git a/src/utils/ptr_vector.hpp b/src/utils/ptr_vector.hpp index 34ccbda32..64a5ed089 100644 --- a/src/utils/ptr_vector.hpp +++ b/src/utils/ptr_vector.hpp @@ -1,18 +1,21 @@ -/* - 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 2 of the License, or - (at your option) any later version. +// SuperTuxKart - a fun racing game with go-kart +// +// Copyright (C) 2009-2013 Marianne Gagnon +// +// 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. - 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ /* * I made this class to work like a regular vector, except that diff --git a/src/utils/random_generator.cpp b/src/utils/random_generator.cpp index 7ff3ff72a..0105e700b 100644 --- a/src/utils/random_generator.cpp +++ b/src/utils/random_generator.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/random_generator.hpp b/src/utils/random_generator.hpp index e5f3c0123..c4ccd52d7 100644 --- a/src/utils/random_generator.hpp +++ b/src/utils/random_generator.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,6 +21,7 @@ #include #include +#include /** A random number generator. Each objects that needs a random number uses its own number random generator. They are all seeded with number provided diff --git a/src/utils/string_utils.cpp b/src/utils/string_utils.cpp index 2897c8bb8..f1bc44cc6 100644 --- a/src/utils/string_utils.cpp +++ b/src/utils/string_utils.cpp @@ -1,7 +1,8 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker , -// Ingo Ruhnke +// +// Copyright (C) 2004-2013 Steve Baker , +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -112,6 +113,23 @@ namespace StringUtils return filename; } // getExtension + //------------------------------------------------------------------------- + /** Checks if the input string is not empty. ( = has characters different from a space) + */ + bool notEmpty(const irr::core::stringw& input) + { + const int size = input.size(); + int nonEmptyChars = 0; + for (int n=0; n 0); + } // getExtension + //------------------------------------------------------------------------- /** Returns a string converted to upper case. */ @@ -246,6 +264,18 @@ namespace StringUtils } // split + std::vector splitToUInt(const std::string& s, char c, bool keepSplitChar) + { + std::vector parts = split(s, c, keepSplitChar); + std::vector ints; + for(unsigned int i = 0; i < parts.size(); ++i) + { + ints.push_back(atoi(parts[i].c_str())); + } + return ints; + } + + // ------------------------------------------------------------------------ /** Splits a : separated string (like PATH) into its individual components. * It especially handles Windows-style paths (c:/mydir1:d:/mydir2) @@ -643,55 +673,62 @@ namespace StringUtils // ------------------------------------------------------------------------ /** Converts a version string (in the form of 'X.Y.Za-rcU' into an - * integer number. - * \param s The version string to convert. - */ + * integer number. + * \param s The version string to convert. + */ int versionToInt(const std::string &version_string) { - // Special case: SVN - if(version_string=="SVN" || version_string=="svn") - // SVN version will be version 99.99.99i-rcJ - return 1000000*99 - + 10000*99 - + 100*99 - + 10* 9 - + 9; + // Special case: SVN + if(version_string=="SVN" || version_string=="svn") + { + // SVN version will be version 99.99.99i-rcJ + return 1000000*99 + + 10000*99 + + 100*99 + + 10* 9 + + 9; + } - std::string s=version_string; - // To guarantee that a release gets a higher version number than - // a release candidate, we assign a 'release_candidate' number - // of 9 to versions which are not a RC. We assert that any RC - // is less than 9 to guarantee the ordering. - int release_candidate=9; - if(s.length()>4 && sscanf(s.substr(s.length()-4, 4).c_str(), "-rc%d", - &release_candidate)==1) - { - s = s.substr(0, s.length()-4); - // Otherwise a RC can get a higher version number than - // the corresponding release! If this should ever get - // triggered, multiply all scaling factors above and - // below by 10, to get two digits for RC numbers. - assert(release_candidate<9); - } - int very_minor=0; - if(s.length()>0 && s[s.size()-1]>='a' && s[s.size()-1]<='z') - { - very_minor = s[s.size()-1]-'a'+1; - s = s.substr(0, s.size()-1); - } - std::vector l = StringUtils::split(s, '.'); - while(l.size()<3) - l.push_back("0"); - int version = 1000000*atoi(l[0].c_str()) - + 10000*atoi(l[1].c_str()) - + 100*atoi(l[2].c_str()) - + 10*very_minor - + release_candidate; + std::string s=version_string; + // To guarantee that a release gets a higher version number than + // a release candidate, we assign a 'release_candidate' number + // of 9 to versions which are not a RC. We assert that any RC + // is less than 9 to guarantee the ordering. + int release_candidate=9; + if(s.length()>4 && sscanf(s.substr(s.length()-4, 4).c_str(), "-rc%d", + &release_candidate)==1) + { + s = s.substr(0, s.length()-4); + // Otherwise a RC can get a higher version number than + // the corresponding release! If this should ever get + // triggered, multiply all scaling factors above and + // below by 10, to get two digits for RC numbers. + assert(release_candidate<9); + } + int very_minor=0; + if(s.length()>0 && s[s.size()-1]>='a' && s[s.size()-1]<='z') + { + very_minor = s[s.size()-1]-'a'+1; + s = s.substr(0, s.size()-1); + } + std::vector l = StringUtils::split(s, '.'); + while(l.size()<3) + l.push_back("0"); + int version = 1000000*atoi(l[0].c_str()) + + 10000*atoi(l[1].c_str()) + + 100*atoi(l[2].c_str()) + + 10*very_minor + + release_candidate; - if(version<=0) - printf("Invalid version string '%s'.\n", s.c_str()); - return version; + if(version<=0) + printf("Invalid version string '%s'.\n", s.c_str()); + return version; } // versionToInt + + const char* boolstr(bool b) + { + return (b ? "true" : "false"); + } } // namespace StringUtils diff --git a/src/utils/string_utils.hpp b/src/utils/string_utils.hpp index b1fbc93b2..7b3063663 100644 --- a/src/utils/string_utils.hpp +++ b/src/utils/string_utils.hpp @@ -1,7 +1,8 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker , -// Ingo Ruhnke +// Copyright (C) 2004-2013 Steve Baker , +// Copyright (C) 2004-2013 Ingo Ruhnke +// Copyright (C) 2006-2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,6 +25,7 @@ #include #include #include +#include "utils/types.hpp" namespace StringUtils { @@ -41,6 +43,8 @@ namespace StringUtils std::string removeExtension(const std::string& filename); std::string getExtension(const std::string& filename); + bool notEmpty(const irr::core::stringw& input); + template std::string toString (const T& any) { @@ -90,7 +94,9 @@ namespace StringUtils bool keepSplitChar=false); std::vector split(const irr::core::stringw& s, char c, bool keepSplitChar=false); - std::vector splitPath(const std::string& path); + std::vector splitToUInt(const std::string& s, char c, + bool keepSplitChar=false); + std::vector splitPath(const std::string& path); // ------------------------------------------------------------------------ /** @@ -411,6 +417,8 @@ namespace StringUtils /** Compute a simple hash of a string */ unsigned int simpleHash(const char* input); + + const char* boolstr(bool b); } // namespace StringUtils #endif diff --git a/src/utils/synchronised.hpp b/src/utils/synchronised.hpp index 8fba6a189..0794e76ca 100644 --- a/src/utils/synchronised.hpp +++ b/src/utils/synchronised.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2010 Joerg Henrichs +// Copyright (C) 2010-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,11 +21,18 @@ #include -template +class ISynchronised +{ +public : + virtual ~ISynchronised() {} + virtual void lock() const = 0 ; + virtual void unlock() const = 0; +}; /** A variable that is automatically synchronised using pthreads mutex. */ -class Synchronised +template +class Synchronised : public ISynchronised { private: /** The mutex to protect this variable with. */ @@ -113,5 +120,21 @@ private: void operator=(const Synchronised& v) {} }; +#define MutexLocker(x) MutexLockerHelper __dummy(x); + +class MutexLockerHelper +{ + const ISynchronised * m_synchronised; +public: + MutexLockerHelper(const ISynchronised & synchronised){ + m_synchronised = &synchronised; + m_synchronised->lock(); + } + + ~MutexLockerHelper(){ + m_synchronised->unlock(); + } +}; + #endif diff --git a/src/utils/time.cpp b/src/utils/time.cpp index de032b412..68a0e24b2 100644 --- a/src/utils/time.cpp +++ b/src/utils/time.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -17,8 +17,11 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "utils/time.hpp" + #include "graphics/irr_driver.hpp" +#include + /** Returns a time based on an arbitrary 'epoch' (e.g. could be start * time of the application, 1.1.1970, ...). * The value is a double precision floating point value in seconds. @@ -27,3 +30,19 @@ double StkTime::getRealTime(long startAt) { return irr_driver->getRealTime()/1000.0; } // getTimeSinceEpoch + +// ---------------------------------------------------------------------------- +/** Returns the current date. + * \param day Day (1 - 31). + * \param month (1-12). + * \param year (4 digits). + */ +void StkTime::getDate(int *day, int *month, int *year) +{ + std::time_t t = std::time(0); // get time now + std::tm * now = std::localtime(&t); + + if(day) *day = now->tm_mday; + if(month) *month = now->tm_mon + 1; + if(year) *year = now->tm_year + 1900; +} // getDate diff --git a/src/utils/time.hpp b/src/utils/time.hpp index 976f70210..ea4b3222e 100644 --- a/src/utils/time.hpp +++ b/src/utils/time.hpp @@ -1,6 +1,6 @@ -// // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004 Steve Baker +// +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,12 +21,14 @@ #include #ifdef WIN32 +# define WIN32_LEAN_AND_MEAN # define _WINSOCKAPI_ # include # include #else # include # include +# include #endif #include @@ -37,7 +39,9 @@ class StkTime public: typedef time_t TimeType; - /** Converts the time in this object to a human readable string. */ + static void getDate(int *day=NULL, int *month=NULL, int *year=NULL); + + /** Converts the time in this object to a human readable string. */ static std::string toString(const TimeType &tt) { const struct tm *t = gmtime(&tt); @@ -85,7 +89,7 @@ public: static double getRealTime(long startAt=0); // ------------------------------------------------------------------------ - /** + /** * \brief Compare two different times. * \return A signed integral indicating the relation between the time. */ @@ -102,7 +106,19 @@ public: }; // compareTime // ------------------------------------------------------------------------ - /** + /** Sleeps for the specified amount of time. + * \param msec Number of milliseconds to sleep. + */ + static void sleep(int msec) + { +#ifdef WIN32 + Sleep(msec); +#else + usleep(msec*1000); +#endif + } // sleep + // ------------------------------------------------------------------------ + /** * \brief Add a interval to a time. */ static TimeType addInterval(TimeType time, int year, int month, int day) { @@ -111,8 +127,9 @@ public: t.tm_mon += month; t.tm_mday += day; return mktime(&t); - } + } // addInterval + // ------------------------------------------------------------------------ class ScopeProfiler { float m_time; @@ -128,7 +145,7 @@ public: float f2 = (float)getRealTime(); printf("} // took %f s\n", (f2 - m_time)); } - }; + }; // class ScopeProfiler }; // namespace time #endif diff --git a/src/utils/translation.cpp b/src/utils/translation.cpp index 02b92aef6..47b6d7471 100644 --- a/src/utils/translation.cpp +++ b/src/utils/translation.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006, 2007, 2008 Joerg Henrichs +// Copyright (C) 2006,-2013 2007, 2008 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/translation.hpp b/src/utils/translation.hpp index eace82029..c7fc7d58b 100644 --- a/src/utils/translation.hpp +++ b/src/utils/translation.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2006 Joerg Henrichs +// Copyright (C) 2006-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/types.hpp b/src/utils/types.hpp new file mode 100644 index 000000000..b2c044f21 --- /dev/null +++ b/src/utils/types.hpp @@ -0,0 +1,35 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// 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 HEADER_TYPES_HPP +#define HEADER_TYPES_HPP + + #ifdef _MSC_VER + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + typedef char int8_t; + #else + # include + #define SOCKET_ERROR -1 + #endif + +#endif diff --git a/src/utils/vec3.cpp b/src/utils/vec3.cpp index caab87088..6595119ba 100644 --- a/src/utils/vec3.cpp +++ b/src/utils/vec3.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2008-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/vec3.hpp b/src/utils/vec3.hpp index 211ca5458..77d894369 100644 --- a/src/utils/vec3.hpp +++ b/src/utils/vec3.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2011 Joerg Henrichs +// Copyright (C) 2011-2013 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/src/utils/vs.hpp b/src/utils/vs.hpp new file mode 100644 index 000000000..b97a6a4f4 --- /dev/null +++ b/src/utils/vs.hpp @@ -0,0 +1,12 @@ +/** Visual studio workarounds in one place + * Note that Visual Studio 2013 does have the maths functions defined, + * so we define the work arounds only for compiler versions before 18.00 + */ + +#if defined(WIN32) && _MSC_VER < 1800 +# include + +# define isnan _isnan +# define roundf(x) (floorf(x + 0.5f)) +#endif + diff --git a/src/windows_installer/logo_slim.bmp b/src/windows_installer/logo_slim.bmp new file mode 100644 index 0000000000000000000000000000000000000000..99f734ceb08cab27001ad938c11f34ef4c650ff5 GIT binary patch literal 25818 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 literal 0 HcmV?d00001 diff --git a/src/windows_installer/stk_installer.bmp b/src/windows_installer/stk_installer.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2d5dfcc930b7f1c6562de12e3f5bcd2fd9361e65 GIT binary patch literal 531830 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

=Dd#?kQxC^VgD(N`3-dgixy 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

Sg&}~zuoL_S&?+AXH;1W`Hibfud6v$ZWo{ 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 F{yzgn#OnY6 literal 0 HcmV?d00001 diff --git a/src/windows_installer/supertuxkart.nsi b/src/windows_installer/supertuxkart.nsi index b3ec37cd2..d3c3196d4 100644 --- a/src/windows_installer/supertuxkart.nsi +++ b/src/windows_installer/supertuxkart.nsi @@ -17,14 +17,16 @@ ;-------------------------------- ;Include Modern UI - !include "MUI.nsh" + !include "MUI2.nsh" ;-------------------------------- ;General ;Name and file - Name "SuperTuxKart for Windows" - OutFile "supertuxkart-win.exe" + Name "SuperTuxKart" + OutFile "supertuxkart.exe" + + RequestExecutionLevel admin ;Default installation folder InstallDir "$PROGRAMFILES\SuperTuxKart" @@ -38,6 +40,11 @@ ;Set the icon !define MUI_ICON "SuperTuxKart\install.ico" !define MUI_UNICON "SuperTuxKart\uninstall.ico" + !define MUI_HEADERIMAGE + !define MUI_WELCOMEFINISHPAGE_BITMAP "stk_installer.bmp" + !define MUI_HEADERIMAGE_BITMAP "logo_slim.bmp" + ;!define MUI_TEXT_INSTALLING_SUBTITLE "Please vote for SuperTuxKart to become SourceForge's Project of the month at vote.supertuxkart.net" + ;!define MUI_TEXT_FINISH_INFO_TEXT "Please vote for SuperTuxKart to become $\"Project of the Month$\" at vote.supertuxkart.net" ;Sets the compressor to /SOLID lzma which when I tested was the best SetCompressor /SOLID lzma @@ -60,14 +67,14 @@ Function validate_dir IfFileExists $INSTDIR\data\*.* 0 return IfFileExists $INSTDIR\Uninstall.exe 0 dont_uninstall - MessageBox MB_YESNO "You can't install SuperTuxKart 0.8 in an existing directory. Do you wish to run the uninstaller in $INSTDIR?" IDNO dont_uninstall + MessageBox MB_YESNO "You can't install SuperTuxKart 0.8.1-rc1 in an existing directory. Do you wish to run the uninstaller in $INSTDIR?" IDNO dont_uninstall ; -?=$INSTDIR makes sure that this installer waits for the uninstaller ; to finish. The uninstaller (and directory) are not removed, but the ; uninstaller will be overwritten by our installer anyway. ExecWait '"$INSTDIR\Uninstall.exe" _?=$INSTDIR' goto return dont_uninstall: - MessageBox MB_OK "You can't install SuperTuxKart 0.8 in an existing directory. Please select a new directory." + MessageBox MB_OK "You can't install SuperTuxKart 0.8.1-rc1 in an existing directory. Please select a new directory." abort return: FunctionEnd @@ -88,6 +95,8 @@ FunctionEnd !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER !insertmacro MUI_PAGE_INSTFILES + ;!define MUI_FINISHPAGE_LINK "Please vote for SuperTuxkart here" + ;!define MUI_FINISHPAGE_LINK_LOCATION "http://vote.supertuxkart.net" !insertmacro MUI_PAGE_FINISH @@ -103,6 +112,7 @@ FunctionEnd !insertmacro MUI_LANGUAGE "English" ;-------------------------------- + ;Installer Sections Section "Main Section" SecMain @@ -155,8 +165,9 @@ Section "Uninstall"redist Delete "$INSTDIR\Uninstall.exe" RMDir "$INSTDIR" + SetShellVarContext all - ;Remove start menuy items + ;Remove start menu items !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" From 65a7bd0d843720baf6148b2fdad12caf6aa320ad Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 8 Dec 2013 20:25:11 +0000 Subject: [PATCH 026/170] Huge merge from trunk, part 2.1 (splitting commits into small parts because they keep failing for some reason) git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14667 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/fonts/BigDigitFont.xml | Bin 3386 -> 1696 bytes data/fonts/StkChineseFont.xml | Bin 155798 -> 132522 bytes data/fonts/wqyMicroHei0.png | Bin 51717 -> 63783 bytes data/fonts/wqyMicroHei1.png | Bin 63758 -> 73450 bytes data/fonts/wqyMicroHei2.png | Bin 68275 -> 81138 bytes data/fonts/wqyMicroHei3.png | Bin 70197 -> 83184 bytes data/fonts/wqyMicroHei4.png | Bin 65546 -> 75923 bytes data/fonts/wqyMicroHei5.png | Bin 36709 -> 20164 bytes data/fonts/wqyMicroHei6.png | Bin 4768 -> 0 bytes data/gfx/snow.xml | 7 +- data/gui/custom_video_settings.stkgui | 80 +++++++++++++------ data/gui/help3.stkgui | Bin 8190 -> 8212 bytes data/gui/main.stkgui | 5 +- data/gui/online/change_password.stkgui | 47 +++++++++++ data/gui/online/create_server.stkgui | 36 +++++++++ data/gui/online/lobby.stkgui | 51 ++++++++++++ data/gui/online/lobby_settings.stkgui | 32 ++++++++ data/gui/online/login_dialog.stkgui | 59 ++++++++++++++ data/gui/online/main.stkgui | 51 ++++++++++++ data/gui/online/menu_create_server.png | Bin 0 -> 61030 bytes data/gui/online/menu_create_server_hover.png | Bin 0 -> 67543 bytes data/gui/online/menu_find_server.png | Bin 0 -> 61377 bytes data/gui/online/menu_find_server_hover.png | Bin 0 -> 63945 bytes data/gui/online/menu_quick_play.png | Bin 0 -> 60414 bytes data/gui/online/menu_quick_play_hover.png | Bin 0 -> 65876 bytes data/gui/online/notification_dialog.stkgui | 19 +++++ data/gui/online/profile_achievements.stkgui | 23 ++++++ data/gui/online/profile_friends.stkgui | 37 +++++++++ data/gui/online/profile_overview.stkgui | 25 ++++++ data/gui/online/profile_settings.stkgui | 29 +++++++ data/gui/online/recovery_info.stkgui | 23 ++++++ data/gui/online/recovery_input.stkgui | 46 +++++++++++ data/gui/online/registration_info.stkgui | 23 ++++++ data/gui/online/registration_input.stkgui | 61 ++++++++++++++ data/gui/online/registration_terms.stkgui | 39 +++++++++ data/gui/online/server_info_dialog.stkgui | 33 ++++++++ data/gui/online/server_selection.stkgui | 17 ++++ data/gui/online/user_info_dialog.stkgui | 41 ++++++++++ data/gui/online/user_search.stkgui | 24 ++++++ data/gui/online/vote_dialog.stkgui | 27 +++++++ data/gui/options_input.png | Bin 11564 -> 20751 bytes data/gui/options_ui.stkgui | 6 -- data/gui/options_video.stkgui | 10 --- data/gui/soccer_setup.stkgui | 13 +++ data/models/License.txt | 4 +- data/models/rubber_ball-icon.png | Bin 25563 -> 24054 bytes 46 files changed, 822 insertions(+), 46 deletions(-) delete mode 100644 data/fonts/wqyMicroHei6.png create mode 100644 data/gui/online/change_password.stkgui create mode 100644 data/gui/online/create_server.stkgui create mode 100644 data/gui/online/lobby.stkgui create mode 100644 data/gui/online/lobby_settings.stkgui create mode 100644 data/gui/online/login_dialog.stkgui create mode 100644 data/gui/online/main.stkgui create mode 100644 data/gui/online/menu_create_server.png create mode 100644 data/gui/online/menu_create_server_hover.png create mode 100644 data/gui/online/menu_find_server.png create mode 100644 data/gui/online/menu_find_server_hover.png create mode 100644 data/gui/online/menu_quick_play.png create mode 100644 data/gui/online/menu_quick_play_hover.png create mode 100644 data/gui/online/notification_dialog.stkgui create mode 100644 data/gui/online/profile_achievements.stkgui create mode 100644 data/gui/online/profile_friends.stkgui create mode 100644 data/gui/online/profile_overview.stkgui create mode 100644 data/gui/online/profile_settings.stkgui create mode 100644 data/gui/online/recovery_info.stkgui create mode 100644 data/gui/online/recovery_input.stkgui create mode 100644 data/gui/online/registration_info.stkgui create mode 100644 data/gui/online/registration_input.stkgui create mode 100644 data/gui/online/registration_terms.stkgui create mode 100644 data/gui/online/server_info_dialog.stkgui create mode 100644 data/gui/online/server_selection.stkgui create mode 100644 data/gui/online/user_info_dialog.stkgui create mode 100644 data/gui/online/user_search.stkgui create mode 100644 data/gui/online/vote_dialog.stkgui diff --git a/data/fonts/BigDigitFont.xml b/data/fonts/BigDigitFont.xml index 771d0679b3289448f1449b066ecbcb9229555a0f..41536824d498e77ce15cc7648d01d6e17a4e1161 100644 GIT binary patch literal 1696 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%0ASJiyJ~Tum&kS(c*FqG&oDa*D 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˦!b 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 tDEI$kRkbf3 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

  • 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 V8mCaZ#1!CuDafcwS4tZD{|{)1&wc;^ literal 63758 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 r*2BV4)gBBav0-0un1{|>W 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

    $9jZ2u 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 diff --git a/data/fonts/wqyMicroHei2.png b/data/fonts/wqyMicroHei2.png index f7f31e77e944a78a579ebabae8aef196c6306a79..871009e0dc53390a0ad895cf44d098e3ba2b3ca8 100644 GIT binary patch literal 81138 zcmb?>Ra{hG*!3B@TRH>@`P1Evgp_obw17wsH83hH&&@t}`>gYO&f5D~YsI|NRK~}l#sL5TUsXj>2LMo>A5j1-jOXRTcm56lUIMC$ za(ceGf4j}2?HAHy9wZbLE=A)ORHyW&9AC3Ii4&8G*I21jEB#do#r~66SmasMG4l?U z_^A&@Srkd+!b`zIOvdE;k%Z?zb=>51W@aNg+Gzv1uAI2ckE>QMX8fv1EURe8O}$$- zW4jkU7grn*kSl2i87*(Id(1a^`++r0J&P8W0j`Drzq0C*Q}kSW#7F<1Vvr-6;iuwc zk!(^FBIgh6KY%D~rO_yQB}6aA9E^t_T5SKfLOXYFc>B07eqy?Pb#|4wEZvLnXce%8 zKT;?lJury#wuM)ii*H?@(p{+%6}^VuWb`=v-I*F*8rq=uH(TC`izd%P=BoG>oL(GU z={{JA?X7tRM(GEcBVtpi*N#jv(|V=?JZ2L5YTZdGDg5;&b@lb>7-Zc*y%6Br?fisd zvyuKn+Z{}R7|DPvU@9)Htc2yl2Nj!ftT1}Xp$<3eD9|;au(EQhYK;&i`rB^`1=}dUJ!;<`t1?5k z1`~+$p)j27q{_;C_1jjohXuK7T4-2cU}a^c+l3z=mD*}h=F@kI-WFb)drO7ABx!j? zu<_1&9Z{6?ylavfxJ~tadU2mO(n52S6ql(J*ao0nOeWwQ`mBQFna{|_&su)}3bAp>wE8bHL6DyBTX*AN_(2^G|%Xc*bex9OiL=C-{GP~@)Q{5Z%>((^Qk6@#v$O%I-tKYlDZ2@yab%zx

    eeZFSqgu32Jb1br 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

    qp)p=^As_2^{p27JEJS?maY2zu^>)G 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�lxq*OH4;J z4h8Mjt+|gVhKgZ?ibeqfoFNk1yBrTf03!{4ixmRzTpV!c(*wX~PTGBOUpK{5eBcNg z++FvOYIqs7TDCWks1s%a@H>cyu5_)c(HR`oo4^nB5?)EDz866EM(axQ+1!Ta#FMr-&xUeYQ8_xNk$LCZA%B?Mu@VkK9G_LfHwDNN!8~Hb!|6;wZea zvt)0eSDWGlb^E!b04cSRR=*~~qD*|H1mSH#rK9!7+0SWF!|f7;=%Sy}{jC8R_i&9| znB8AV!E4{I4O=5soBegT@hMb)?d#Or_OQbncF5Rh9EjY$-h87YJJQkkdvr60Tb>t^ z)h>24s^E&k9~#VCSIFU?(Uy_}feeArVww(_&OExopaHUpS1&>VE2e{cP7oMy#6 zOp{qy_}?3k-2Q>mcjy0bOM0i0bpV3Lck-PqSuY2Wc1iDqy-GhZ!h_;8v_gK1%3 zxMb-0SeV!kJ+9yll_0$n`8e%WF+s+fxNdgs&lUWq)c5+|vvK5bmAW4<h8JJ!%1Ny3fF{9F$NMKl{-H9@m`E$V^5+U4 z(8Sdev*#;%r?OQ2=HKLt^f?JMioOB3XP6P9cYIaO zc49n@LV|rjtnc{JV;9y>1@7-P9k5nID;C4z3A*i=@=t8yOKrpkeV@f#9=eq5p7$iZ zYx*C*h?BFZbUsy!*>!~nzo2hKm9^iF+}>AuJ?Gtd!*-KX7tWTZxu5LcS~#aErNJe% zypBb}eb{`?)VU*wJBg(lRqD~=Q|8PbQ(Qp_8Rvg9?B5?rjYBtD9_^1 z*caDMjyc1l0W78ww%sdy{v>MqO=Gk^HE#R;DNZK%9hqsSW;z?@k>~59*v%d8wzMqZ zlDv$QpJ#Fhx=&KY%*z9}%E0b2-wa86{$QXkICS#m?L#Jg3vc{xqL4x@!N~wc&Ep-oZ$+_zFEioCX*_lOv=)%a| zYG+U6?@&zcU+k~Mf6HlKIC3A7(DG$Q4ctzNzv}fFqdxvtheB2X`GXnDB}gwC1#ZH; z-7DT@d&F^2emLy64U5@1m`Ewi1!hauqfk7F#mQ<|kvm6sj@){OOjAnEHLj1kEd~Lr zUage@#Bz(~td1S2RJy|1@*n4mi2q#kP=5!9ebOr~Ct@VVHY4bIYQ$)?$wc>1dnAP3 zWZ^~Za9bwI_K6F3l(_Bi&aVyzGd7pvxLnW^_{MRKXnPMotc78VWN-p*_fal8As~@e zqycJ|cTl6Z|Dbx(lfKY!2Pxa3=3!Wq@7aFDyNOm|F|(@@v)woRc{i0YamdXpt~EB9 zY{P;-?n#ZjaH@3wy7zhRr$<)93Qecpo8EUc(hytYsIei7vnSJhRpRf86G<-jqwP3l zWBtFm+|a^zF$i*E`l9Fd(!&2WyC;UvF1UN*Iw-Y%^GqP5F* z6`3D^XY#1E6sYn9nzS&LQo8hFq+cr5*Pr=`PI^iIwz!X638JZdC>Tk(X}+Ns*-wfC zh&1dvgE#>`=()^sH1N^#0n_#}(@}eyxQ=VZ^wwOuLOO=$RV?eULRL#zeU-x7~35}{C{`rQv}ZK)j* zIylXkwYjEShM0ndt0hUl!Y@)?7C4M5A&1k%Cve96PP^Bqg0r^UE_s}!OdadYjomO; zIrI9KmsdMm->}yYm~z^yxu?=@^nLr9Fgoux_N~PAxFW4SWBh2hL(bAr*M0lVAgjf! zJgeK;$etPuSpVa`*j4n+uO6iryysko`d)u|2b4fzI}?%I)|IU)?HjkE6mq*$c{8ro ze|&Fq0~a{P&UVmcE+bfETa-$CzoX;i(VGrADSO-Z^o{soF($)m@~~y~c02Hhnjj4C zhe9S+%+{D4SD}~&!pe7=P^^Nb>*lfUw%h|rxH+ivfnt1Ezkc!EKRu7(+7HXMu47Tt zc*fiPs2;@@luP~N%W4joXZTx&lBLNN@r|}sXEd=A|T%%O~{d~Tdx4K~nWP$-N37;(G{M=S9zNpaV{&ZR4 z{6*-NmcoL?o;R_jex|pBKCDq`P8sRFl(+S*^|T`))rkjQ3S{d}qC=N=H|a9OI#25+ zwQ47u5Wsuy*}XUPfDkduiOCP|wwvwMjr_UE*@{hOYK^6zGSsxtdq3ASo*Lr@Lgr>E z7Fe=5xHmpYQTBd=F)gg{Upe>MDx}Ypm*3@fVI<;~K8+JT5+Uz7#o%S&? zhQq^QT%4rw*DC+owcsBDq6Efei|)c((VE1^-%5-W93jgV+hTsHCWo)0pGrSJ$qxF) zgiSS-5(8S6ymE#=DzShUmYab- z#R15DPjM{(Zl8SmZ_0^B-dljW-rTXo4~XYzt2>~}tgoFdrjP6BA~3>P%87| z3*}WTTf}&uwK{5w8(~M78xrhD1)=MS6%lQ2^wkNf?Vj+wD&fFtQ&xY|x-&iaV6BdW z3H$o*1V`ubFVeq;XUgzK_Gdh~sbp@T8XVCdD$2MAWKh!Tx@@V+$WOp!vxjTN&n6mo z9P>Gva(l0qj=V~P8|6UAi(!p${)c$4Oq|nfYqow);~7#vjOe5Ll7B|iTtllJ-F^fr zTPp?C2HmT*A>Yt8h+Ql0?N=AgO6pI-u-_FbR9QP1n*hdc7HFLOoMg-~Do?bAm0w8} zb^w0KQ)BH?A-0EC0qtZl0ZbM=#@?;!ItH+hIW=%>?_ERAp>k}_gQT#Y4<%38tJ2$% zM&I>_>~D_brumJi%?zH6#LVgukRx?r9P;fWK6ZB4Fh1IvXv_F$#7zcp4Q_WI|105A zLf{X}e3)bBff+Xh?Fii%r!>9^=%E!+Bp{Nragr-y6m|x5tZHSXO>P)^7^T(MDDbY0 zY>Rgk4`my9bcCN)ikFjvhi1|S%#pM-{~jY? z@x4!~9ri-1=xMqxMp0sb7^08J4CaRvsD%z#?$1w-4u#<$nDlhm$kc7F3#yBzVmhPZ z&~Hn|(t@wsA3%ydR3aw?uPJb$>yN|WyCs0J`znRHEP-af|4qS>l@4n<04BK%(#o?O zM%0Js5dARZ!5cV=Kgj{Ct&g6p>fmo8t^UR$Uk!_-aa$r`c86b3Ts_E#c8Ef&=>m^8L+r)8t ztmNG%3}b#C%Tzst8~b00=cck^w&EOE?-sE5D!I`LW(PMH@m4ev9~eJa-e=<;_`nA^ zzUBdDz>JVj8A_e`I(s)_ztO39oRT}+&aev3@M9i*w|jSjHSO zC^(wEhKMDi7sQJK{uCVOHT~R`p77A9-5eJ#gdTZJJ|!EcUf7Vz;X3kzpybFb86muS ztk=@Y_~0Npv5!!vj|z7L3k;D%ym%6CME9GN4N_$FR0RO1PSgCWFpkiSNz~|MiFByA zAe=KpfQw%JJC6|h8Ubx=ieod~~>T?8v z74+gvvzHu%scozXJ*HaNvfQYY1*~qv!v0Ru+9#drGv|SiP@c{^0M5t1%F+*~u2W^p z)g{uk;9OnotkQ+Br4I#1ro{`#=F#25uVu?pz3F*2&x-m_zuxg$F+Qq|U2wXT02o|z z%}_AFc~6|kzp`8UnPioOP`Q)$i-HUQxsqv7DQ50&s8zjS3?x5gnW`Ebiq(sMI^e%O zGA1XvDf5$IBwb$sfK>^+qE;k$U_$zEEe0j3BU3bZWdvY8m8vEZE+(4P$W2w(D;n-b zqmO=CgjfgGmxFTOXGgiR+S{HPDf7pBC5Kms=_ptlGnUH#eJZ@(KFQK$KC zf*?JIS9`YYlR=HiCr66Ypol0gs{Xag;z(!o?-$5lOhoWigs_iJDpuEDiD9w8T2y#D z^!Ix@<%Y5YdFGeO-a$gPC`o~JnfFG(#~Ihrgq=Aq1nz=@3j07m^x>`L>efWss~a9m zhKd=B{dTd7H4{p4JEU|i1<7GJj!nX)*)$C^T!V}$QlsArSVn=Z_d z{**yA2kciXF&GzTXwJ`3)^Y2jNFMRw#}Z8(kvPqj%L2xr7xEpdRr*4qbfB2BjMazq zuwWlq21k%3%>>Z}pYKJO$*~>J*U>WB#6R+w?B7n&SAwbhJk)csaSR{L9Xv_xUDR7T z0d)V~^av%2!I_+$c71-uP|?x-wtMAqII`p?PO$uakh4aXmZ8-dfXN+C874v2sfNY6L;m^zQ{g`?k>nglj$F!{K8y`5O#T=6#A(l+V#HFrdp$IE(jUSwo>TVIYF04 zxb)rc1qkMe$E%%81+Umh7n+GKO6$d2aXce|7O`$#blH6^SLS}cl8FLwQBkkfQgEw# zXf}1$xyz6WP%$(sKIJykpBCX-HlA#C>LIr3u|`+P1JqLo+Sczg#*m*8q!FEFX6b)x zX$jh@H$}-k;famN>^6GA3D>{1=aEi#0a4=dyV7)$vU6nXnD0-T`zO^0&l_wqxdi*@ zD3~Z`t~_8y26MtOuMOnBqj%Uy6$;sR?FN-1d9R`Lb;Kci1pBXM7~|phFx@TgAe$yO zOHLkRHzIftat2I-frd!|Q(OKGI}ynz$jz?MoGFZ7$kWx@s4Ge6qCDS~(*#|5ZYv8# zHP9gWajrBjukxFM6E03$&GV=!<(B$SXm!cbvb+_*zFFXe^%A8w{^)cx85GtCuilOv zSx6dEFXuA^FB$)G;3C^N;-##w#vPTI@G_SFGm;ZZmt%1TQ(Qmm6~;EAHzp<6t-K$P zoU5+>^FyQ%$l_rr-@uMC)!ZZv|DkWM?AF#&^o6>o9CX~ zVQ7Rx!@u9_gPzQo6e)WJj7IH5^2@T68d5)}X_E;PHYK}enD~zE)E-w@0{k%GTacYz z-f_|6{Fy}B2Y-f#k_XA@=a3G`DoTAvY&qHd4M#5g$qxJZ7F@JkyAD)3#pxqYHJ7BB zzsmbWNWKM3t&)GiH1Q@&#~;%U( zPXsN^=u@YEb|xOFC?e;asIGm^*7_q7;1rqmzAx+(QoiI-EMIdyb>Xy_TG$C!$g!SH zS%_UM4=O(p6F^$s#ud0SsSm5Lf1w}$5;==c2vvn}z1bOhS25z_c27%(m}JT;0)`I1 zsA;~Vui`wJ*DgLjj$l^RC4%SdQZ&y=*bH57A4Gk6Xr&kTV$UuWV0N*%%J25=9oKyh#v}12FZv7iRb4G!qJACfM z;*-UxbOg91{rfG~DDGAc3)z?JkoJvLq|?%`R^v=~&`*Sp9K>+niCbKe9Um8-A#M(M z`bG2zTF_wNt~s9WIL1nL7n@&s*Ae?g#BM{}<~ief5U9&=B|N;~{+coU^$CtDqIYg2a274O^0_^>NcbHC>l;&HzK@y_!hOW}=Lw z7AV}zkT+M|?!$7wPkv|gHAeaX2x@`tNN@Lh2>088l$zcdk{{-1f^J&WUb0BXLRd>_ zHS1Tb0S}$ikI_xvsKVZJDyZPy0W8&?D9B?QZq0)~wOna-9$LR=d&Y?XG!(>t=9i0L z1%NvHWEj=X&|PCb2zbuT4=2^2$Mlx7fgprRwrPK)elLs}!uZZ(-c(~tWVQ=c2W^4>kyEs`f9z0BrbZ*ryngRExy?hr;g=|v#|bwDr!I9cFjrkn)!|BdW4yk; zFx;Pb4PRRQbmUg{t>KeKj;!@aCJkDHqHo+PhlP)wp7#xsE6LjvdMje?Vt*wIfH{z= ze*L41-N>q^jo_Q6_kTNslCZ=NrrMNt!a3-p_LKIfz3}0_?`=NAK}WJmGd!A>{;U7C zVq;Edx7{!hwGk%J5bcmKCKMtNva>mvEsP0guz>Hu9`b<$m67i%7uO>3oak$`+-$DlQ1Eh1bd zI{Uj8JjJcV0P*`NZbtBS$qiGIypmsH{^Z229|BRJ$+s)cMnBay9ChTl5+1T|gyDsPQWjEwn87~7Fj+f+5;R$z^X_C=P2rSPR(EoT~?6tK_xT8yGI#+CQ}DSst& zDH?iYEwwVPW6Ap^bVJ4^_5NL9RZscQ$a z7wu!jUXbXJ(htd0o?&#{@{nq$inryX8nJ$cRC;GU7Cd_Y`TzfU3}2hi|5r3n6(atB z_}__%7X#omY^V^+@C;fnGhsC0e+U-^SunitU`Qa7+A|{|Xmwu?^C7TvdPFCQg38OgSJ@ZpVrO~#(w8!I> zY^z2+Evz-*C9+@jAB|0@v(~{Ar19?Wm$5mnMZ>9~JVvM77G6#JqnZ z!W=KoJ_8?Fw(B~RyI3p2Jp){rgqQDuq- z)=j1?(lAzK1u7@oEoUBck!lGGH*L_Df}F2JvA!gx{~Lx?#7MOiS10(|6T&FQ)y$)H zY`lt+8#gEpp={kl1p$wx8V*J%e_zAP%(iHWWI{C$xMi|hH>@zCW6qMDoLeP#iKkM9 zm+9kXB2@6Hy1k@w&0s=tB|}z~r?Me#|HB1%iMJ_Z@KcBU%z0;RL{JV{sJUkip8j3z zp=rRDn=J)%?Ev1=^2x3;lq}7}=%xp`j;%IfFwV7XEYR&QtU-4*j%mQq`W9Hl(QU`L z&DG|RXKl2eb-UCfzZ7e`%#0Jd*05L;j{N5_^@`c`KR-X$&#yi8==CvqC83!SKt!vnW#aOR~6 z@R;Ji3S6UpPoIi2h3o2q^`JQ=pz3VFYA=6JFhq0}Ia=()1SRd*BuW^xrb3EpC356; z@@GhD97wzK4qA$O@*8&r2yWqH4O*W>CCk#KUi&cGlbL-d{y1A(m!PRG7%%+*dNvHG zFhayy$KCrPf~*@?IGUE&+&Y<@e)yFCh;*WfaGU1G!=HS5ykUe8?ejv6&SS2Y&i;6S zx>_VirJB)?$SV`{b^wkDa3$n2(fT6LCoRPq=2hN(aT|Usqt3@57P$G&_r3L5b zzE91<9%SG99Pts)-0gSkll;xA5sD8FmAWt~o1FbL?G_f*^Vfl&h4Go)RaDB|!3(tg zeNI^V5Z$i}%qHaY6UTZxhWDgfZV~l4Tct7|lcNMm6z5X!?yjtVM?01MayNa$BCYUe zt{_LZiQ$rWL=+`*#J*&)*o{-P`ZwuEA?$!!12%%H9!G8~1j#MBRSa(}Ow?L7rql;k z#gE{GWW0N{8_q8uYGOwpc;x5bkN&}ZE3fbgI z&l&V{)!YA7kFYn|E|_tTnwZ^a1&iHBY;nr|q=Lgd?FNel*#Lg162-_&QW3~#!2(C9 zDLWqk4$dNt=tmBh*sTz=Zdm{)@+pGom8V>A)Xa5tOs1@^LpbT{s9>XMt+#beV_}DN zpR9L`{44a#wS6FVR+;zv3^HKst*W!?X8V26(g0niEytvK>Hd*as7+?uRH%-W(d8h0 zj8)ITC+j3{bZ4J8Fd<(qx-PK&U!!a}or)tJg$L%VG=NOW=MLey zE2RjClON~c@xaI9X}8>1{$WTLIozk6tMKXlG$Wnr^wTZ>OOrTn%~d9u)OZ@mLkULm z5q@|*Uh&R>Nb)Nkgvx%u+N7~67R*@v!&d&|{krFDm_c>cSnv#Hy5@cl}d#Z1B z6#DIVC6&Gm7WE~(q3vj8Ps``{WTtzu`zCBurxUMRxOa&t2s;!DJB`|&2AdRhA=FWW zU)34M`S`2dh35eW;Kyu3e5<1Rd{Ecq4b#@k+9LAyRe>Y|`}^RmGs@jKFEQF+QlFkx zR%Q%!TQRM^|8D+E!F@vmcq z{hYnH!vWOj&Y24L#0S%pG`?n(YYR77kNAhb^Dd3r6Eg=1Ke_SgyBX7dO5N76ock;7 z-}GKD#EyAq;hq+c$5#;n=;`z~sGz(>re^MC>of!^#Fm2FT=Uuyo8Mu4qc=!N)5NSW zwpol70UavR5-z`B5A2L3#B@y@d`$-C&$cbIJf9H89BS;D-T%B0#j4FBf&2d?2n|>D z!74dKsmF=dW_SKkNaNBm9y3dtKFUl1JgwngT0G(=8teu5#Ra08Xl5Is36Qr*ay0s` zIJ4W~qvb>0_ULn*AMv@jqEdKRV{eR`%*aCq=7HGJ(CWX2l`A27Tf`&<MoGnSfp z1zxVI@g%}97c1;o9>>>R%a1psD66)P9_b&coVQHH_>*Rp-=vtSWOZ{h1j#9sxL7-TyjZdB>$t=VUcmn=RLjYS}}m*v-cRyLu_G2fW(%nLWexo;iV#-{fd!^juS-kbsYb zf(JFu9XjP(e4{FDYn{I95n78Y$}nY>-evy{({FtDj@KYf=6i7CfLTCgb{SRWqa7oE z)0oBi+!O~>WxxL)lMfnGD{glPf1%M%dn4d|?!hu=GEd_&tP`hRpz@or&=Z409&k+? z0ri<%dcK2_(h5U16`=;k@)*(<=@HEK)k|Cuhhci^s6UGVb~55M?ugL%Us)ughnV9% zFW4e&A`Bf6+%+R(A3>~})DJt-i-66>CIw!fA)YmOt*^atyBp{Tvnbi^bUq`nF=)8- zNLXp>3s$P|Xb(*SYe`jcR(n?){32w7%vkZ$D0QNHwAYz?#oPMD#qKU^%xNK8WP-%g z?J3T0GMi=+bt4qlGuvBrB1D_pV3)OH>E! zxVD!Fl8MhBvlh~lDsC8#1g?FTeLSe1@H&Fu*`^K6W}gMR0z;SKv`>56dQ<7!hn8Qy zB!sOYq0}&aAR^!DiEm=*PQRd1Y!Im7$T_}lBm*fFz*+!YmiThI(_nE^Gq{Sq{%~A! z6i<&e2E66c8LV`%1ymOu55J3f;EtxDbz6Yw)y4YXxy;JA;JrN76aE-6|CQmPMXNwo znuy!QE;W@-HVYY+x(EYQTx6f@(n}!OOMaJajWN1&FrT2*_eqh?`p2t)rHlD+X@LqO zXvwp?%azU&Iy?M87de|U`;zE{hluE$?Q6}o)&|dX8)9s&AmW#<=GHFnLAvQj-aW66 zFP+-r%Z>76jIb*F?4p-Ctfu^O^O9nc_5D9_JS%@%DclBCA`pM5c((%7GGf1}DS{Nd5)UP*->7GgUEi9NH;FZ}fV)^cR@^JD!p?1X^l`3d^Okl)mZ3 z<)&rt79L=r;;fL`jdseL#!EBt#`z;PyPG7;=vw<|9qs;Sr653=fF-ZV!6~|x+))?S zhCLQ;U0^eQp4Ql}a1}4;u8K_%p;=}~NZN~qBs3MKsmK25NPn!=$7uNpjRP45>}CE{ zRUIRsBjbV$?cTnmMT53dMk{gT6e(EeOFvR%9d0oj3m|vt!xh%M3NwAVg?$%=KmQW1 zf)iQe<@|Be6KRVz=p1?2`K-O?qc&J`s9zOVzZ_1BR* zd^BOy_^2q)fOc|+(pj+|e)(mYG6Qu{CW}t`WN#Y(Wy*bLTPvWOZFw|0bSouC6GEGn zcKANfwIv4VOzEt2ZNmE*K|zC3fS~I%y?jMU{ zI;q9Z!*qa43DiS+PWzzrHv>}1| z9*wXg$sfvD%G-}OPKZDIUg$EFA$5cWkRvhh07Sa|FbxQHYWQ04GmMpFlhuklr1Scr zOhaLlssA#TL>hHII(0*JJMrkm=K;AG4Mx+#Mu|oI^#Dqm!UJ!mEd4eks{6yp8@jJf z5hZ^_g}r{r{ z{YlVQBLb#+*0(7ee7`f*QzXjm@>40DS~6MKd%g*kgv-mwhjR0)e?#>l--sm{^B8Nw zy4DFb)Ac$3v_HbbQ0w3s%Yv1`I>;a7EpEJn9^v;OHMm7k>g0 z`;0<)DJd6$6bnU@iT&J&a4#e{fKLej%L&Kh7HhJn(O?~lT3{RvUH44KK_tlO?~`Ro z{zNX^qL!^A!fM|7?VQ*Qbf}OWx*1XH8Jsxcqcaltk8I0mXLZg4EG1f$alnAR4HJnDkExHxlcp$Z!zh@& zdfYKq>iC9b57?J>J!OE<eT$YXBih3!KTM<_S!Z_5WeMP;8TLn|FC&dId)NVk^Y-wTc za_Oz4i*@h*-1VDTa9q9V2%~LyzS&n$(kO9II4ImYWuR6`W>CkSpMZWsjj)7hlGZtyGab9xqL6UrSulGipCZsyL9G|# zS&-^oHc{%PU-G7zq{t+&vHyyvT>A#pr%;OL$3U+0|B$l(+$><;0WCN&MYHV|=Qu$;$4*CPm#FiBsLP;mOVR%}j$MqW$Q+$onkb zYyK~WmEuNv6#0L9VF6V|6v@qLX(*6Rza})#69K0?al7I|ostw7Ul&nqtPHTka#39U zu^WWuq73!rc3^g~B}MX#AG4kS9T}h`Wg48a-k6N7vu~g;^RXQQisjedMK+mV)xrlv zpfv!&dHz2sN%;i%z9?N)08X1s%M*Ttjq0Abb*5!}OU-&ODkas!-;$^g+f!QaIx+;s z_k`32k+e}ycMt%1QW6kdygBQoI$4Fa1ornL3t*|V`(T|E5%grdc}9e2rUJs5NL0ki8>&l^ohIk3z=WBnq(8$6hl`zej_X0UqDR`?cF-%73udvx)6VmfZlA zwxO=|1O8a)BQuq#i3&1ATc}avQaKkhMj->9F}o}P*E++oHjKs?t=Z!?0Q9?)2r%-F zO_(O!ONAneM@Twl;rBSAHE(;Ay;sBZRiz_hLmltRJ!y{Z{NqoRQky22m$JswzL!Z& z(KT%pu`37{%R%-Yy^EjvoW^&qEbHQPjo^h`o;w4!J)EBL-eQ0dJ>swxmZj;+peJep z#6G-HiHIzmEPJp+0si5WRm_y-e2{ahc&$s@zYGx~YbvQ{87HJrQY^K30=T=yRLoTj zF@O$@x@b%s?**C}@oR%+{3=nnA>qkqySN#3O%>q3t2`IP1bmG5Urme`o59sFwpkE8 z9aL=MK@989C;*5^6gKtS=~8hSQKPX{J^(0uH!)7)H2nq5(L>6!2G~q9xvE-o`jy_%6|?sA1r**o?&pX&9;yG7ppeD|?c(qs?>CrRJ~e>V-J<)Ro^~ z=(g#SJ2_H*x`|Z49$YrttxHheRKQRX3T3r?cR*4WN@>7bKpgtM2|mB-1--H-E>ij) zS@&pIaL#GAx|2Y#$4dRTjE^@*F_F0*t9z37q|to&ymC}Q8hL+!2(U^h#d7(qL+o|{ zgaC0E(|iM_5t`ey3s)mQufrklJ=?fD%4lv9E5NUvmL~M0YG2f@I|;FFAw)?aOg^Ro z&y-4o^Kou5b&JOM3EotH;lT{d8tXr1Op{cwH#~W3W3S`~`NWQzzn1MSDI-=goDg%k z3~bli|6?P=!BcA0IP+?@@Xi6Dk=KG!XYtg5J2q|6l5(}xP+y(c-mq1GHM>~jOu`4+ zO}3_uJ3{vR{lzoNl^d-)j*x1dal7-U>HSQHvg!M$q3JP}lSGBNw*uqeA5#r%H1`KT z3dpzAO_q)3-u&)XOp)jDWm_)SpP99CqH^7As=T-?p4>9JDse70fYn%ZENUBK?gbLA z%(z6HG$o(=q266*)cEWgtbpD2#8sycOFqRtmad2D-)l{_8SPJ+D3?K!3I{PJX2E+h z3VYv0YcoiEZxCov4&BG^@f`f;NVdWHgX+>?$LMUsP6bn7PM zIG(uZiMzna008dDX%Y8;#GBUXC97|mJr?%BTS@|7^<^xl_a#igP-tA^4@u922QbTI z!=RKafgGMYVvu0#WP?0!uSj>TJudp2XrZz{++bvtW;ruWz_m*Ev+<>82hbTg`uKwy z#whR>trAl(x0z9~^Z?oksmaJwK%)FZY+jrZ^~+dT z0*tG%qcxf6z6?9c)B^UN0xWs5he#1BftGPGDH)>%5}gIowqw+DNTi!xSgg4i&Kdz2 z?Qe9UQgt69;u%o#&MyFP8u%`nJ>@cG9n#V=tW`LdTphSnFW{$Iu58jRzx&QZS8 zrmf{z+Y*GDM`9Ui&GOk7JC;swdV60djH7UqNe{pFqj1=N8MJ2}XkB`^!#z!v{45F;wsSf@i|WC?Xd=Wy_BkQZ29paAPNl^HI?&76iz$Pv?mas8Z&R4_gLI?rFikt5 z<-eO)?+R>ad2lEQcwIP>D8!L+#=DK^rBoH;^uHj*3{DDX-U$q-{}XHv6LwRni;p42 zLI?k|Hds9QqY^)=?37tDqel0mDkb6clhk>l!6CSrqhIzRONbC^pA5ndi3#VYSwN$f zJYRE-=Lf0;^DLs)Ura)9YSLIj;zt&<4NNeP3h+xPGz>(*+SCqBkT`}C1(qfi{u4Ce zb+g&pb{>P=jf`SpE8q1ppbz14$Tc=p*#vEJ{x~qNEiJrbIyz5W&Eczo(XV_#oEeX% z3w3~D8ii#cu7`riE3;hzd6^Lc7B%^$iU)0(6Md;0vi%z#yn04pcy925kT^!Y_VOz{ zqLD8(%7RDHL2lJI@6&33;t9EOw0+SawU@jbqJ?nUKU!GH~ z$G>@sC=$Sc*Yl!R-s;o_ociDSaeRvfU6g1dOTA~_vRTT}2C{b1{2XC4-iy!q32AS5 z-)e`F9iu0;h$>Ha{Eaaia70KymD4YmaSrccSUAe&cRBIEqAKA8aO|Fi{J! z6ppN~O#@b-Lc{+8cccC1`BkN5!qzZL%_sfB!(}Qv;#$k3$IPVUX+9iey073BL0G;b zHRnA<;7Jyyzh0__k4d3+6?YLc;uLx6#K@8Rxem~{B3M*hJ(Y& zFF2f%G;rlLep0TBh&U~K6=w#SlmJG>HTs7nLl7U}L$KkDOBt+xSnP5thaWf2GTIrK z^UcV;#wohSnVet)d+_h6JIFh5*efrT;xhj1!k@6q)f#;akqUxzYh)c012vJU)Gc(z z?OJ?%z&A<*LI$Ja4)a%oY86&{_Xhf`2y#}^;c_%r)~o*Ax60|m$w&(Pe>64hqK~h# zJGDbsC%{qP>?aL>|BQqihc1cHGNM{#WGp1~8-b8BT%B6g;ZD%AC zIn#9s!X2g%_i8qiCE<;dJ<8Ql&v$7BRajnpDXk_-LLic)T-o+U zuT_l3N3NXYHS!u?FnE7{b;vu{#gZ4zLy-uFfP$f^+wf56^PvYv9oAl4c5W3f2!&vH zp*pk-X@yuHox$((iF2Wiy6C**`@a3#MnX(zASIw+n0Hgc{0l1pc(Tpl&{Jl*M{J(J zIz=1@qj#NZHP`X#D< zuYtGxKceL1j0iSAm`k{HvMOjc%vAoMbp^d1wC}==(1&LMPv4T4=)E7LURrv!1Hmh@Y{u() z37CYOkW|N!xrjqgdbO*mLI-hdP8*9{h76Dla(SgOUaQ@X2in=$9l)VI`EhlNC?my@ z>dJxFoT_DfY;XBpc4FWF%JrK7w{kk_kfS;vz7I?bz%OZqQLGOyy!!G^7&5AV+snOf zd}hp;;=^{pJZI9}v*BhHOI>U_kJEHKNgnU42R?cFv6w ziq=;ZUrzd!RhrwO3IO9Uj-TR_Uu>r=`wN=?NTByy|IU#U7mAk`hUord5@(DyG#q8B zZ4*#8Pwg9Z?srJH_HhAMucIw|Bf}aZ@@<|d$-nKs0DGf{BLI_0IU82ugVBra+*pAi zDocd=7__c+raQC6r>tA7VijfN2>yl2w_8R$A|Y}CZUE zhrTiR*Kdir;UGBfEKHF1jOTBJ8hx2(6qHWW4tpG&D(K9W=jC=Y!YIim=s<1w!KXu zQzh#<`;26=6*GnI9b2rTf{YUFr^%d$kLikBw&-1^Fvw&MxDoIOU-@^@^F#v1?3VP~ zz9y7ISb0LuC<=M^k>D3EHK&DsuLsN)MWW|FA5o~nQ8?*h-r*O9P@bv#qz!&)s#dQhG1RpK zW4ve?7kEicBlmclTE^~G&z_#0msxmgtacds_$W>50c5Egv^hbbqpgt&ACF#|6=VdyV{D@E*cz) zLyJqHcyTDM#fudvPH=a3D-@@=ySqEVp;&Q;;L-*Up5kxr9pn86cYn`^vy*W$_FnT@ zGduE-wbMlZr<{#G46bAb1)>GETO8fgREt&SGZy*K3Q_ucQ9Ed+d8qrg$_xI;+wrCg zsM%1=o9Rw<<2PJB<(iL~>Y&62!z%-E2hrxqmGt{p7qrbezldX&3&ZI^;jfvw zXNU4D%vRJ7EcH5_e>UO6WJx2*z7hM&x3|3xNe|Pd?`JZYChb_zXN}^-4C12DD3U(2 ze)Xcot4Sig*^>MVcQs933is%Qp&l8xxFt&XHKvHIgnC_4gEo|A#woC5vh~6h#U#t> zQur1GbHCy|vojLTpG1uccT@lq(rZr;b>GJ6TL{{%Q;nVq4#__rHn^}P7&dFB>`n8CS&?R)5nB4}`+w->hU&M>D;f>` z(_HXPuZGism9v%)C)P_+uhUi8aa*}qgfzP@!l+u(#kmq+yQ?LEF6EIA!}sG%w)6l& zRSw!eNSrLuc3%!zz^*UUo<0=NjwRkHM-QO%{#L^x0{CjW(R1-(C(%U9CbQOS>|JyG z@3rD-x6SB!UIwzVz;lZZ?C-b(6&OQuBK^QEnc-T>x!+cR^XT7ev+(Gk zdKF#k_%-_Xl#IoDO)eW>(xq02Rz|-$nTU z=d%tSpuCuQ)Wu+KX@6pE8Rh=_=BKPMKC)7dG?nEPd9XHY=cr3vb@OfJeI0v!EvZ*8Jx7c}*`Wq0t#CCtX z0FfGXzV96Jq}GP{E!iCAihtR`9&U;Mj=J7k^gmsDA;_8UF&S+_OuUg=(lhSf7m_hb zrYp_HX*MAa-xTB;Y%ij6G)e{6Y3TN+8Gxq?-j*VK?AVIeFeKTgKiOjX_XTycnb{p_ z2D9nW=ICG1uhqLmNb?9$CV;1W9C<>JENC!WwPA8GT%P^ed!4Ja*5lQ6BiUH3GvE<@ zfxWI>J>|y9F<*t!`BAlYW0A*z16JH@K4>F1A$HI`cpH#Ku!2Ipx!Ob1IN|opPGYpo zkL3J-2N{{d*}=d3DH_iwF!D-R@OWmXw2K2A5y<>fjsYw3MwdG7}A z6zeo#EGku^-WF^@L*YPGhdu(7hG#y(3Xz_Cra=(%BGfy-Jnmj11Pas(Bd*tN^QY&0 zl#A#YNRT8o8t0px7Qn)`;wU8^$2D*abKPzqnc|soaT#8E&4RJhs^d%#;OE*j-X`^B z|8tkb{pux~7Ub2vvibDrE!$90;tGK`gA)4hZilsykIP;EmH+MarH*glhyuT-lYGa2`_Xy+8?_R4 zY#Q#2ESoSu0IYcUh>4TxpTD9?{Un<0q4{o!<9hOlodMe%g$4>v3cphJzgy%+Elw!< z&SWhK=B2u{VdVyvGHPdh}FxZ;wIzH${|I6V7wOzfvh!*At3-BN9THwtM+*3f!SmE~nEZ$82QXt~HcWoTGp zW#YLa8D(kPsi5#WhF`ewz5W_jqxwc793k!QrPwKM?_wi#`XivXFi@CY`5@jjOC%rl z5QfG~iAO3L9iEWyS=s{wb%AbQs9h)QMb{Gr)?Po%$gXGA#~@2> zuO$5}j)Tnx3{^r)|LJ(#5grSss~dbKJ{VIE?-SgTQ~ego1rT(< z&pVGS3MSC5k51^1PcK9zfM6 zxy9QBPrZ+QD)$w&AGd$yiS~}MnwCdBlKtcXT>FdjGF#rdq}IP5Be_3JOUVRbHd%VF zh`#0?qy4tG>m}w!q%afdVDpa&`rQ%51t@j1vB#gZ1S|${ExJz!(EllG%T1&7Vdb7` zY-25{y=Du8vk#>D5Vsa2(qN!LFbcrys+A3}jV6mK0eCMf0soOzNG*0m^zqZ)jQlD# zXTZl-t&6mV*G)KapLJ#4JvaPvNGVGe+5zdv!YBy~pHX3YhaE3h+u)!-(Db4jw)fy$zZ=#*{IO(Zs_YTCA+S0%< zyPVh|8jsnnhW07*m)6#ovG2FY{W0PB_Svq_18Un8>sV>G3PRi=UGe*TtBM%_QTJIP z1gKcdwq6S%?$`pf`y&TEx+q!jFx-SEj4cD5SV~)rw#$^lOXvr}FrijP=&Xt(LkI%CZH#hLTAw;b>bE_=Y&@FK~8o(UK9S z+c8VsaoMuuDBkBS${o}8N!0$nqO*vcrS!RFY5zTHhfLx<@^MX}U}XY*wyL@2!>|~r z(N7BQx{kBN>@Q|VLfQor#^P?l4Eh#58vmQRop2gd^EhqQmQcLP%Q6G`q~ClRli?iA z0qnHr@k%YhU%j=YR{hZlzZar1thx@A?p~D!10c zzZ>wWePXot+wIdU#*|WS#vFQ;(Fd6uqb8oZV*9+kxR3}tA_tTIC7rW_rx{z2=0wJXEp__#3u)mx^ea%nE^tY)#9CZJ_D?yT2enAT!bz-Sd5W2E)hyfdQX2PKc!I8q|CisC3Xm`vZ90YO#Z+IJe2+z` zC}fFa5rJ}_JW9$>Q=R;Do(Lo^yV5sfbWsB{9{J=6BcV@c?pW%d`HS&;C$~S|@nqSZ zX>6Ml{mY&e`p|T#ef!8x>x>68xdj=2ei8~;|wiOHLXO9|(iM!_<`+Z$YweVIQ zgmcZA!xPF!^pw-&|UO9*E|POnJ@L-SD1HEO+u)P2;+Ba>yIaYn-bdBbC3FPfRdEFo4OcFUceT(cyM4zGt=5l=b{ahEl6EbVwro`6! zjgbJt8&BkzPl)KTa|9Q=75=OPYR87t0s=!ww9)LZx)_7zk68)U{6-QCBUtyF^iGJC z+*amvfX!Gx?vqiRJ1YJ@D-bL@QT?7~uIy)MqX)4uO>b&Mdb}s`Zv=#(JDUl171H4C zmE9HE^Y0I?HWH&u9U=B>97gXV+$5VeNUkUYUJ!POSyjyd)LgUYizZH^{>JR5X#N+l^_KD zhO2Um#5*D{bn`6{^@MktP+D=KegFovpz|%@r6_Qluuxp5%g#FLr<^UAe4VRaU^dbB zFwnSKgHTgQ22UK&_F~GLDR3^j6uOnc1WxRW?6}i|ZBaJuemNwV3mHY!+TW)oOU@1H zQ5>s7T3>E`ID4_CcoWSB zJIl7d-LcIo+vQuIu^@QfA=_U0lg^!|xVm;rYBa<6S9}C;L!z%h5Rzb9R1$yPV_(cD z_o`@X=V6EAqja+LlzZS5_}RJgwn^el_w|`XT)~v%!Vd6)^+6qmnhQ$c{MGGRC`AV- z%xwyQ61Y<;b-4SH`OTgu08tg_N88HUU;?3azCv=4D4f!@x!56?h=Ho=^B zRviayO`-gTCdC(gK9JXDWL3ly*rw#Ow_*r@f#j_XID~Xq>LS#S%srQJL7AtRNcx|fnS5O=``7x00@_nOlamq5?`U|<4#c2l~E zRwp6Iz3X7S@wk?7Gc16CUd;j6;XbDNAc`B*0WRPo`_MiujA@Fi=6tjo&4td)bY{hj z&}%C1aN?C{au$Zg?4agoW}I)sk*A2%;?!;}9$p9O*31wKeB-eCcN2#h zteb(?$02lXk-DxOPLQxILF`N(i!(Bd{JUp{#cS-V=lc6mfCCaqZG1;kJ)5t9q@ao# zud^tnjk)wLVKGdGeY4EN6SYp!DawM=L*LEu*sxA z?e4||E~s0KC^CDV@s`-znUA8z5&=)D zctWTGbf>QIl&u!478s%G))G*g;F-UQ*=EI7`H`}( zAUTbqcUqH-GI06!()V2bPZH-eD7N~zJuVM^Kya!T5803N^VC0BzJH$wsPwo0LwgYA zMD`q8L0-vGSxX6Jz3{PIAju{c@nMkTD9v-L{P3)^NhikmyvO=e&PnO2HDBFPb^ZP` z0Yvuu&op2ubDlPZ&q(*yo72d&LK1SBvkXKyH-HgNp82626L|R%$}N&eSrI=G9aX*^ zyjunDO{Ek(7knXl&Tb3AzCXMZ^cpJ_MvVCpKPZoA5a)w47(Ho8)83mO$_y1woBpbT zWKip+YmRtCf$Yro^HHC}(XO_uxGpqx(d^^Q70+vspAm79jYsKYH(H^dg?jFS;1Z*n z&=gZ?5|%Bp8Lc$0Dd)T(x{y1&yW{CB+N4Y*|7UE1w@EQc$P^7n)5=39 z4drPZ-B#uBJ#=`!V99d3A$^Mtazxr)Mc`p?nZ7^_^N6kUK2D#F?c25rY>maDyyygY zQtcxZsOCP>R@RSmNG=Yf(;a6!FmlOe-vSjQ2K_kQjH#wha~Vb5Jmq664KGaO(=Weu zYu;}Hq_~(-!#Dx;g+!MdyB(_T;!g2j!?*ic)p-`h8n=GufBd3NA9_h~9Ok=LF1(1n zo?O@Mgx^{iix~kB!E8fmsCh-vJ)^{1>%q}zU+=WXtO!Lw==a0%m@2#dbYe{XeXYT< zwVp@g6O!r$Z3i~Wr|!h=D$D5g>P32+P^0Srctgtbai^1O*WbvPGl>rYV3Fam*@z9U zy5UUz;yiwWlYYi!k0@KWQEz#)k1TO?QdNt+;o`J8ThUj*xpII_7>vSQ1g~?gg^Rf*9qIUDgIZmgb$wmU(cq(4NfX}5T}ds8p=P)ZTy%zwF}Sr z0u!nW0PKgr^7E{=qwU{vndNQ|vYvxAufOzrt5nk>EWQ@c1r zL#dvkrZA4e1YrS^w9_k2pSB`s>+iP80EN3A6){o~6x8H!DZU89Qxh* zpU@+3st>K(nfba{^C<&k#d&;p{FWj3<2|xlGl&lvEl)a~{{~vL^~7(C^~Nbgm;px9 zYlZq(`g{<@ysaG#dI_OeNNx%XVZfH@!VDbBXG>Mq)Qs2Eyrqv{G)M2oX+BiiqjwJY zXu8P&YrVNvSZwDef(q^c4QS>}DG>G< z=(zhwvuBR-2UPx3A1Dp&gAf+uSq%AXbq`Wiq{54b;p*$e0INrlgYrLM-gan3EY4h| zT#3-?^b){R2t1j&Eol@#>)5&0*zyZ+7I$j0P$7Y-5EOK=}e;4(;LJ^h*#`4sZoA3w%^fqFHTgB?5Z|xA#<@y zg=eC5bo+=^np}ES0D;dzUHv!als=^Qq$I_5HoN zWIb;Cs+?2WbVqpN7L08^j5K@On=~P`sz(-_r~MhmiA89ce}|w&eP!^BKk1z|XXo+% zEP&4Y;@kPNOk2jqs`g;pP>TUffBA$@?g*_kdm@S`d1DEH?GVe^VlPTqtO@e!7`mc^ z$K0|rk~zNDo8@uW>LY{zvWreCHcU}DL0q}KTNAGIy7DOlb~3S*A`kA+ZVkU`$n@6* zO1X15N-(5FsZ#N%>Rzg>3}aju9fmOq%oT2_zi9j_$G+`46Eb^MHn5?1F4$zaeVoSY zQd8eF1oeSHBW&(DMiG@q1avC~=M^z=SGix99?fIj?o|Y>^IT>Z%yzL}>o*zVJ!`Hd ztJVW`d!#lRO6=RRix;6aw|))vO95niZ_4UVT%%4(&KSsUG|Sr_3-X#n>)4JxAhd}xAri&`XWB5|VZyLC0aob|SGLncT zfL@!Ng|~R)N1cU`@O?fjSc{an(*PVHa_DB$lV33opqsnxUNB^hs*#c3Jh>Wf-*s@h zjWzVv8t2j-6W8^4wU}EzOP>=%6vhe0oG@tLKQICLL)|tYY#)~}r*+FLPI@dBY}xG1 zNAm0ju1wZ^#X6_P?S;r{W!BWhrg+WcU2Nm5Af}bzfe4S2tpg$bXF}5)72TOJ@AgsM zhWc91nAS6)%9j!bjoG3zpHc4iOP3Q^BPqZJ%_zu_Zukev)_(LHUh7YnrEIVZN@R^P z_mx-?`t<;e3VSIR8r4R#olZC*c?^jeN|J7d@>Eu2^sAA~De*RwZf~zt(8$>TDGq3{ z{cDH=LQ`7^;UUvxYsA_U??xIyIW5ir2H*N?{pX8+Pv8@mGEE|TaGOL=G<|00l=Zux zXZnZIHwd#gnwClwJCwYrX8C#)f81TR^^MdHx0c^1fr6LK_35OUw0luZVhS0L(}2#l zAB16v^uV4s?}U4#lC+H2JmWYQeUj@jvry7uDVOthJDhZ=2g{P*hfUd0_#77WLsvV8 zN|w}}zHWqofYwHDMwTbfvfGa3Yo|G`k1BY70g*E5&yL?U>7}`e27kjGeUop zw9{x`q#Q5x)U8rStq^{LTvzqBa-j~wYtuH!SA8)+9R6nNSgNqK$J!(8%~|iBJc<~@ zCj8cj3_-yAl2iB^nI)-y=;!5iq+_aVKXE`4#CuBnr zc9h-1Ujyb~Uk}`ckamJ0V0yrvf=+qrQzoP{`S_8IleCh3AW{Nou=*1RBo^5rFm_?y zt=fR}X*5?kT~KzVG~!>6BES&Eng93!K9BI~|5&x z^e~jgJ)Df>1uXnr_3~-{1io`#b33^!pC;9fv(;N%0% zU`H~}eR0EEYJXHA=q{YGniU$$x3ir+e%ftH?5*M(O&I&Lg$0(PjdGNpaZjHTDBsefgtY@A_j)BodE}~ya zHP%Yn0DQ%Hj7Te=p|Mf44qQ-#hD4%&KX~^n=To(afp?x2D_Z66LMXhKY3IWW*7NTMrpF`fTtp`rDVBI<+EjnHyVhd*j}v4*6l27GgAi)m-AIE#D7Ves$yA z`n{kvC9p7ij4&F?(D*qb;OgM{6ui0BQM{njjTX2r48%ovu`z>wVmwmG~c4D<0 zt}GV91C|&(1afs?4p?wKyosfM;y~if*UHY}#56+LD5tumdfosw@Cj%_Gy!OlW02OJ z7AI$2;K$Zr{tFc&5Q-0pi`jDv;)tH7>@X8(HY}#Sd zNc{ocURyGf>9mc&$_EoBP)~v+e_Joz==M>}1fXEALXq-kf}t`mqCF=dHLBpw=roUj zrz2_MHH&3mIDk(<6H*s$%6IUsVhKxXK6Mp!K5pgel~6d6O4dC0=hoX0r3p)t0~12N z5#_3K2c!nf{y%`g^1y`{7&$-;`67MHPpZyPL(cNC1I(j1FLOW8q31YFiLZ`!CX1`q znO9sDFujh(GC?2;8u8;)!yZ)`%8Xq$c1M$C*<>5?UUv*0H5QOAcem70O!wf#DHCqU$s z=>Ocvtp+AC!7{_u()5rgm7YTU$E7%dGi0BjS_ykg9z6A3n=-{@EG<-@f^nWkrI7mj z#FwcKBrby<1@}kV)br(7qyp9hH~$GYK7B@lr?sFsy%z4n%3p>2uTmk>AKd4AmpwKO zO9+qZ-a8Op(|Ab5koL$Va;khr^+7y)c1E^QYY;lx7loCBexfXyW7LzhT_+h6BIN~ z{tV>2wuo~0N@QF#-~jLr;eU;|&xEvH{H1o#uL`XP48_|(2MVAF)|l@tk!DFXHGy){ zv;zTn!S$+j-?puo#L0}bL7}G2hn_NwsQrzXZ%j>WgdKkU5_=Uvkl}5mwNQhNON+o< zxQdFOPCY~3#kjbl4B*uTMM_8*f`cXw$GqKde+vZbiPr#4v{TA|a-#D9+!v>as~mEY z_sq)b(5FVZj!Bvk4lyyByW|$X#`&FQu)_-bL}en+85g}C`zvT1SES}P)el(_oE;i6 zt3v6%=2~3vzS8AhN*cShBqN{%l=AFO=m>->Vi+H<$h13Eqz`v}QT$^*%L?ZQJ~yI) zrzm`H48jB(ZwPu+$iJ0ypiPS03veMW4E`ihgG^ExC25y=Wc|Hl!s>ssy+F^binIA~ zF(A}&w(-G{-dqvDZD95pcEDQW`T$0vXE23H>f4($B7yHnzN9$jXQY;Pq>t|^n-C45 zzKQ1IW#=y!3s2BTQOk8V0&<_gs6&sMKLOIK#gJpo?eY7IcRI+d$t6UCO$^p#BU3_rlfYZjffW5{j z6cUkzwF+n99=ch_UR~C8SF*22fm*+~(u3QwB$Z#@_Gxxa(=>8^?JCPDcmBnmuJ{wD zK1VvebB}$Gkvw3J{)uA5*d7<3^6n_IQ`E7pibcp-3Jbd$?e4nIuHvVt9~U)El;%il zucTpkTeGvI6*T3(4Y63P3xR>JU$cU=hV(@^LeGKo-N}A$1K%rlhCd8t^CE2&)fjJI z){d$(1j8nEX9n;@YDw7(W`$<-5JOOS%B=XWe>O79uKp+B#M`)Nc}_y zuVX8c(!^bk%gZ6`1UZGrQ2nvbMXHZ_+wVCRW>44)aR9s~Pu{G+ofi@G9V7t~vbOg7 zwK0oRY%Q0uBX^{J4Gqo~SFSr0s*6GiMCAZOpzjn2`u@1pJ?!xk4}TMPAKRIBw2UQ; zyqET~xI*J(BJ$63M%Immeh0#!dy-)+hqS;qVN$%=AcXXe!yv7`Fd^5s+hFB> z)q&+drgS3?ukW*x&9GlslMF8qFFJhF4{Jxo*i^|Icc>*)QNOSMG{)+1AJfic+%}H_ z%aBw~s|?dogk_oqjSzj(D*{`n(UCOWt4dj1x@67}*0bCuQ+6Hb@#2ak>u`|rsqq(V z8bT;y!Cs4Alyg@e8TZb@R|%hCTK7qXrmdQEz{5ADKH{j!y}ugRNC0A;r|mCB68ck| zyL5BklmX8__sr)6BeF;_WKNHhJrqmc7tVqRfjhI|(OQDNGScmXi#}WP%t)5_fju!0 zqt$ug!$?x3WzdHCb1R9Jqe48|sJ~vNK+RpGj!a&h zH`CBM&1bjve!IWPaf;)OicA!fa%M$?W;t~=68hb9QSFi+d~il%5Cr4MFen)(e(5@`QYP;VBhEaX&hd{*j)d-`wj1}jM`^m(1)2cR zy*aa~HtB&Kv&B`)HItH_xz7q}aQa9zrE#T}O67LnE)#sL79jjNkLyE!DJte>^G9z8 zLva|EKJo>?0Y>xnyg8%+U`6vseK-}t2_f#&!43aGfJ0Wov`Pa5(PB~W}d zlq14%GP53uOhNVCI2Pa&@B$>U!Oq8R&3;j^cj?!7tc1x6UGmF`PoCyxZeep z_|0o-QQdOQfmKblu~vXn1#?FSj2%x2TOI3yF6rOX&3ixS!_Tv@m~LKpDpn8}%?6b6 z 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 literal 65546 zcmb??Q*b2=(Cmq^v2EM7v2ivwPd2t~+jg=W+uYc;%@Z5{cklCk`s>b1&tq5D)J*l% zOr)}+G$K46JOBVdl$DWC1pvVROThuK(Eq`M$Ko3R@I5ChA*$}NeyML5p|^zF3r4Ea zzg9G*n#y^~rlQPcO=3rm(-37s!5D>2@1X#r9LR$bVatTGwxGKOF8}VNG4BIVG7sWx8Wcm z;x51ujZRJFeyrDSly5lTf*rYMwj+k?U1LD8q z_C@1!)ZybxlratA?IaYW2F?Pkd@cn;ybQ1aiwjHVzj?NI?v^D4_5@~lMe!hJ1LK%Z zgJyK$S+cH(vutwSVRsxcss+E6Y3%xoJ!^+%&DFH*3C(R&P7z!=OF6^cThOq9)0EYS z=}>*{lk+-&`RQLhSO8$6v0&mdCuUj{Z6UKL3 zLgNb_^e;au61}JNgtjm|6s8c6uTH{b-ZCS!itb!`<7|**-^Yv1EU`<`E!s7%q+!dT z3EMh6e9i5N$?$KCAAVGL)SKQZ$JyGVT*kkW)|-Dcq3_~B2qym+{^wZc_~YiCW!LRv zE66QNk-YrNKj69jcia0C|DqS-&i#GB0Yejmr0Ih4Z$C?|iMaY#Tty5e>yoW;4R$Tm zKst1A$iMpu2dY|W5Cc57$RE=+kEmf>y7EPV*1feuz$*oqsiq+cI^o#HBW3py56i)U zh|7L&z55F4+emML#_4r!9|VBEVvC1M#b=Cxj*ZWiOG}ZyZp!*ZUNg{Tk*b5q8oyy# zEkL*-_|22F!+z){sQIDCPRKn83Sf$?LtS8a4`)?1v5aLX&iE^ab2F~J#V*@)liME4 zw3?Y$Uz#>++01KV=TFA+UD1M`4{66#(afFRt@W!efM;5KVTa479EQ`@Xc|rSjd0E9r=yA|`qm_;eixT_~ zzT}lEE_@IBoX``E=p~cI0W&(9mJ3ZE9P$_${N$ zoA<&A_@Vg5h`CX|LCiIcQ0`jbE-AG8=m&|^-~)g9ZRjb~M^Pq^ch1nm_$*>VJ2%ic zc;d$NIJZ?6GSMf^nOUI+6*8V-Ew0ln{Hg}^2Y!Bav=i9+Gj=`cBoLre6}vb@6A%;> zWI#AT5Js(sx;Yny3qkh6`*%_nrPfIxeD&P4@-y~lWoN9=zr1Yx{LO!sb^oY|k^JO@ z`(pZ!dYRgI_Y{l@(i8ew&N=>ewK%@Ho=P~Z-~wtkwCAaeQ^#wo?UP8qf_vCjtQqcr z+Bodoga;1*SIZnNnlZGnKC+LuTXq$#OmLMToN#h;HOD%EF+u=%!`+jkeF=);zim~^ z0gy<0DB=a%cDmt5S5RGvOUS|98zI!k7iK(OMQ32(OcVE;Nl1ku+Xr8tpk}`S6(p9y zz~6R}v-h z4gO*Kpx7@R2_^rT_)h;$Wllv&o_>jJ5;NcE?Rg0q-`>}zmk6bIZBVB_!k=E)%=~Zm z=5zKgmq7`I<8R$Vrn7r%&oz0V!CW905i?Z4uhmK9^vaqdtJqr3c$;$MTRVF#j27)O zo?}1}v8o&)0|KWlVfDm-(ieDuJ_!JVQ^91IOo-^~i9)sOsW7+|a?Qve*?AcVjm~pk zFl4iD+Jx@gO?_T|)ctNUPdl?e*jOX`1|of&2{SCVqtfVG14qS^+1q*-=)uThTToeV`0TKLEXTJn5bw$IB}3-Glgvh*0yr6n zO5FmBW4$mu<7N%k{fd&$GW_%u{Z58mf6SBz3UT$LEJjsFvTA4ELeO^g?Aoz^JlIY2e z=z0bejy$IB18rZp!SC!X-#A2!5vye{xS49&F&t1wHxvh&CS9-g| ziYh`J`i)qN!p;syTq zuP0MqV{baS*MHqR_WU~u+_BVteuos-m0L9#J$6jiVf7UJCKT%a9 zme_lH5%~PLZxyS{^ucn0vnrE@y1xWtKEaBtW8SOFmFJ91SBBg+B-mBL(7RjwL#OYu zhREkZRR|p5qp{gKVti9k5grcI)T*tO)E0q4!^L; zF>5*TteR?Cvm@kX*>Ktv-U_5@6J`}ayJ=>!w>U@MYItIG$X>V=C_{(;#=U@<=#PgK z>3|Xx^_b^-R!)!4&L)jrx&xl{XaK+hlYR_Wr+YvnhazU@%v3J;wjs&QV=E%(83K;x z5GIZE`JU|l)>*ih=`w~i$=^b`b)3Rt80Uaj$=0nkRNFovknOvoig9$(aM~*YH^d(c z5FTA3wEG@1^Dugs*$Cvlih=keMPd~9T=&~`O^bY3Vp$^6a4(PjlCLUdIRngYD`$5D zrWnzUv>@taF>52CMqZm>XaR3$lWKth;2B#@Rbi@)QUdw6+;ka&t7?E>_{^Bl8xo5_ zj+@%}NjU0D^d2NCLPG(YaCdK2@P(k(T8Mfl*kx=nhdu2RCmN8PaU+fk{M=wzG2Di` z@hpWKe4I<&E@Yq*ZxJ?xZSP2yS zuDv`1{2xGE8!+e_A*T2(9P%?bZW8JPeUfS!bv^iCqDdFl(y;Jc@i09V;Jz*%S)$hR%BJeZ-$@mDb6 z3TNHYF4SjrAR|Lc7WI13PB0wT)Mx!v37;1v(v#KZj{x&={t7&an}jolZ|_+Z*nvZN4nx4u%GB$T+QXiuXc&Qj>W9im-(Y=;3rAAcavQzl z0{8nE>w!gzUVE|l$dQ0PpildsB||V% znT*;!WW2BEf8B1x@l1YTK*}e))X=A~f)3INdy^&?#jFzqU=@w(ePQ9|a(mE4oAfLbB*q1JKu8Yxiyzooad9c{G%K-}I4i=8W2c}4 zbQNH8;MOmJ@-za}`Z3)2(!mIW({G*vkA3SdW@u0AvxTqBWEZ;Ddm(Lo$2ISJ_dWAN zwCqcoEP2B^s3k1zoX8Iw@LZe(clpCDE7A5sXP_NnG7mF_;$L@MM!-TKJ4x!;P5SD~ z>NX5nvMOUMukpszOfcv_K6D)!)vUy@wX|;D)JFtg(i>&-RDWUqt%LUmd z)^ZYfwdVjvqO^?ub=>`sZOtMd8)IHLwS)IMYsxCNkd~e%@o=A-jtLkgC3_9n<3nf3 za23}Qs|rDACeMkcN3a0;>m=_fS?~T5IMQnQ{hxkanG;r}X8o@^HQDsZgH~XDWvn+! zy&<`ce(=e0pEn2@vWs!jtJ>4iFGWz4kS;dGU&e#n79GSse?a0YdcUab+60G5 zC=eolhzQZLoy00y{E(SyXjxOqsNf;`wJv&|*sh|f-#gOp%928)sY$TJA_BU3u}31glt&P`e7OwQ_XF zkv^Gc&AR@%Zx9>N!}*hyx_LLLTU+`Xy8w0k^_&M9n-x(3zj)-flg`{4(JIT1cpr_I zMWUe&_V^Q~N+aL_Z(sIcH&M6g^oVbRJxIFgLhKa7dm8y4GZ1?Y3XPA1;0Mlp2flLM zV)&7j&rbt&erGY7;>K_G63&7mT5|T7<@q1#wZJtfwS7^!FP8JAejp$$dgLqWaWJa2T=?RAafg`)uMC+0bAhaD~tmUnJEaqM45+gz)Z zGhlU`5EJer$Bp+?`toV6&Ckl-e})^C@es!kTgDGakl;>Lor1G+6`^4HDoGTR!Orut z0YRYwN9EDsi@PuSK8FScJCzDyYnjbMZHkr{qg0mZwRZY;kj&L1p)xvZrhi*L%G^aN0QE7)6QHoWi@m z)yQiInu8Ab#US=;REg7kA z-gmGIdxR8fl8^B&eC$Mov`nd!d`A1=<(egr$jI#=rewbf}CLkD!1fL`M zcM%X2RcT&e&!}gOhc}Sb6dt4?uZ9=!G>w&Q`bWG4%`w(WKf5?fR<~`rK}(R&5f6D? zUbRKiw8$&-i8KCUis;i0&UzrEsIhFmK+88IOyj$#kdvd$P4a5)*jLbkDC@k|JZ{;< z3Y8AJLVB>eW!l%v8aHNd1%r-yt=XIg6gYj0Jxd!&m}SwriQYRvwz&|OpVINEpc73x4whr8Mjykxb+E1m5rX)JNEbBEKUnuXc2Mmtt%L? zYd-)@faNhIPI9)M*Vtx_7Z;>B@%!nYx1+DV4>mSHgW*$K+?UORfBpEKVSvU6lOPQe zOyseh5zY@f_*B(#bkZy zrFMf;d3K|e#moxRNKBfyhqZn?*4gQeeb;#3;FP3Cy|Y_SWlrl!8y$?tI@7bH3`Z`5&Gk#`L#o3BzPknv<)WAREZ&iMlu!lb-zWj7(0W5X zE>OR z50Ln6Y7G$3|ABXghWraYslrh(`gsdh2lyk+RPG_s%DUGv)5jlt+04#Ihf~Jq3s);3 z$AR`+d)B*{lO8;k`{(gbGg&<(ANR?#$#hc$?p+#Ag2PNFhv3TeC6Q%(J=<3y{xo)l zI*X-8B6f8M%e9)mwm?S;SB=xT$Gc`CBW6Xq2b;7OOyg)OhZoZIol8F)uxe2n`rhJC zjYu*ikleYPVr>WrPJ@b11hzUmNh-PtSi9$ko;H;aGp??3sdrq_n6Hs~K=BZgQkcNY zU>mAZyv#Wp=PW~CRd~0}Bs^0PLz6k^(1v!+K=(PqFwC;v=Nx5fQTH%ho6jf(JA3Aw zLKyHxpN6V&<5xTy{Md)~uXHjwv}S{Npd3gf)u&$qHWQ>F)lq)I>zE_k6y^jD8Z}p9 zeM~Jh!g0@GhSZ!`@rPL4M>V!|alkV|ZEOOYTzZ2OoRKPrMyOFpO4hd@b%yw57-sLV z-&zSn-flm7M$(s=xwQEhhvR{q2;%Gy!prC3p?hd#iJ{CfA8PSd z?5rAvmNud?37~+sBMN4}`D9xU$6+C8{A2!K`RI@DsrSLk!R2dTbxLL0Ly92p(8T*^ zWj$Jl=A4nh%JD)Fr`N(1_KR}jIQ)K>yFD~<6t?Q_b3R>uD3ewIQK+ll0{<5}2lsTJkP4aBROv<*!QZN;bgh~Kx3>jVFfHtgFxTF4=ODyW#OKNk z2^%Pj=St|&i|H&U|5dL+ZEu_hbpBa4n489P7?(_3d-^~)G3kxCHc`@}pr?Go8)3Ix zziJq+UBRD0Qp0dJ(0oopXt-`%Y(xWpPvH^nz^5nzvOu(~ z-pcdh@lw5TssW4GQ^>c5M`PF|jk}yVw#VqQI6C-Xf@r>^NUe}Da3H9--l`40w|UwJ%gNRE?1g8AVkLzj ztkRf*o5BD}2%)O{1NCfyu4x0YJ_dWjm8oPTDrKbBKHitlGCJMPA0*<0UN0b!1kc zBhz5r#N>ZPbpU;}_+)(nyMXl4?bibU!(E^G-Z%5k4nu9`rsCgL6(r?8Zp9trI|sWli%fBUF(Na zQ^)aqRfOejxLkT>VUXL`Py*XGB%dExlOaP(Z&I+aOF%@TiVz@-iXhF6KTkxMHIg+3 zXVmPKq7bZZe+DPQlH0k!tJBYDyXP~06=9}%GE0Db%xj6vah&v_ODpr|YRI1-S{YUU z5h@_KzXV6ggkgB*M`W+n)O!MRHI@5QnsM%qrE8{4~Lo*yt++m7+?;sR@s0 z`@5jR2df`3fS4~;hq6A!IF)h)>y^2Q?`>{W){bX&)SOw$m4z>FlRdfJ zC){g7gUvH89}_9!`5eOP(kpWiqW6H)A#;m=ksSJ5Ggq-1BD2C(!Htk5Gbm6bCJ2%Z zCZQ|TR7!G_dpmZ79J!6T%7n(xUK?7Yi<1+pLW@Vqn3{fhd>D-RKgKq0b{?(Q>{-3(ciLh ze*bx|AkfkyrWaVO^fWT@V8=yDcT||>L0LI* zJFvdmoq8^5QiYi}C+Fl#F22hN2Ce7@yOMXv%kyoISf(4dd$_X?$@c-1~8L1nyR?r#`yLtyjC($n`Wx zyBPkSbo%&M%Q7o91Bdd7&41FG|3F)G%kX*+;X3*0|L}PHz9#!5E7@@#u_O1{xjh}R zQ6EefN1Ndv7S!H94{DL^;MZ$--BmW@;jT9M)n5FhC!h_*DpSDBmB;p3<5P`mV zZy?pNlDj!Cj~cv3$Q)!U=-^Msb|jT5?JLMxGD(fu7>{_>DAlF_!0nJIcZcBsr}HUH zcx^Kdr=Y{tr0y#=a}S4(Z!gy7q2D6ma62EZ@p~E@6?M&o_7KxKQ2ySVfR;Me3Vf#@ zRUH*&14wUXn$J274c})mrqP5 zrU&q(Kd3`p(%u{ z8hE|&8D0BJxO!(cf;!SFW*Ur`@GnU+7jOl_3St-3zMULik#7Mot&G9+<=M3hqa|wf zc21TK=OQ^$rH^{0xXw>QkB*0pJ~e=``9n1jU}MIz-9Z(#zuoYETJ`Fsi$gIpu#6w!|Ngliu>Y+cEQTD%`swuxNzSL>-T}1~6<5hbDSR4bsU? zbYyX8f<^8|MwX)ioKf1a)FR?~(2fR&zUwmGZh+MVLPtQ>{z4=%Y$WO*Pa14kmN*m%y%NPY}h6`t!KGuv<7)gkjs9s*3gZITQ9sdu^Ggp|{m8$W` zkxrrH0>#;3UX8hkovGgli(6${Vs4m!>s!F?x;IU_7x-C>@V5PTF}fJLJH_ZTXzWQO zwo*l`b09>Qch@fY^g3>PKEv^&n7^y=P4Y@SS6jPhMyv)Pz-~X5XA*PGYze%>q!J5% zKd}TV228umYF%UN=(E0HC(h(BiX{)Zvav{JQho`;{1z>5zE0WWr--j+H(A-ch4)7y z+It*9{tMYSFh6E_S`w5MdoU7)QqBxomM3XZHId`?;gV_MZ0X#`ZSE3Xi1c}fWZkYd z6R~ZEwh!Rl;ue4IUZj}0txzq3&pGb_{c;p2wIZV4{^CGMsP#o(BNQho5Ud9XT>O?s znj6=U+J)499wjyFscj?domh4^udfBMes<>XVN;Nn9_tZ%#yI3Mi7 zM}n#xVu zynBK;z4J_I*H6r_pGTv|flm;c%BZKV4$r#SBCbxIZ3GmHU zzx&5Ivl675PhgQ{gY5M!-L{X-QHs03s&)Usoz;`j96jvWm@j~pW zRd0=lqlM+ZleCjKCX0_lZ&c`-5I}L|kEO%o%&I=V$5oE$qZlzECZ2c*`xM4M~4G5H|6*?BEG*4IjJPS!(S?zX!brE-Q%1j}@)NKX7h95P~K?8eyM5^3L2w^d-5 z^5crA;ZtjhpUkC4)C}TY;)$;&bxeOiu;=7hz1?b(b{}A7RrzyZg+Vzs-L7ejDnLe4>;8PAJgp87mf`V!7!IW+jaMN3p?)VLN;W z*9xR6^uqK||K}3C2uz`w97%C|_K%V=z9D~s+8S}e8YX+fhEw$jA#As)_){-mL*Z@U z=`q$_fb;l9od%gVuA+wK>f}1N{PGS0{h(Y8tN4U|n6OBtO_%9@H_A%|+W9DTLU&>xuvMJ+~#ui$n!cn}6^g zeVTvcOQ1s7DD!vH9h1XKGBb8#R&Z9$K_gn8BySaM_&9g!(d8$=Cg8Q6n8g5XVZZ0v z(DYx9)nRUg1M)lJ=V;cPJ3VsHKJhG<$BAjCm`ToG%E0%37-MG6Oui5iHs>nwYs=*! z88Wte;t0@W*`xZ(vGooKd{z8X2n@#aQ&Zyts#$_tJG9{RM>7cmc`B~BqB&I07WENS1?oQ%=h` z0r{&{_ybMhV%up{d*uZOxfuLT*Q7NPQ(#bpRDKWm)H(O2ikCUwtQcw14I%$GARE}> z{Al6+KLzwZibFu5gq~Qm0t)%$aMX)BO1`?6;;4;<_-I`INzP#j_LtA5zdovsUWJ%M zsQaWj)xVhw8P^xZJWb5JOmax=lp`dz^q4Ed^mo2jzeWW8^5S$<>N4#lp_4txaZ{%6 zEr|B}Q{5T!z`%|!I$tdxp2su3UM%9+NdPr;?{LI>YxzKx+PI2vt`+(rzW?O@F2(ka zW@!>1!8X09*$ML;-YD)0*`)NK^IbiZ>vLFi8|Ms-WO~eW>~Y{uI4~LT2)uRh!>0#Wu~JDWYQLw zUUXO(O2i9(c_O~oz={eR8zGrJ1QaHO_{*ndsAU{V5nlG?OMNwOZ8x~=0cMIPdX@fy zAWb48a_uv%_<#E~+?k6P9F-DP$kP`{Mr2m>LCKju+x`)m5ug-Um>9U~vMqtR7SpfI z*~(&PKUyqVB|>xOp)}p%KC$-f2JSmiF3qqxJF%nJ+Rl;xZeCyE$2k-Ai8)wS<27w^ zR@|&a=CiEgHrj+qk_jwy*hsei7`@_9o{_MtGVzNUD47$e<(>FmMxAu7LOC#FDeW8-vR_C`}yua43#u7066WM!HB zcshKiexK)d3qj7YTEvr{iF zJ0GG~tnR$^IEeBEZL-4j8cbvwbA;F1AX*qwmwV+IcR@hyoR~CYs9pPHldl@P+c>X! z27euU48Iduq}DvRc?&!nIv?tdP{qu7)tnsUV{3tI-ixEg)hIC_Y~jXj_V+eGFFIbT z-|AYo*sJ76J_OWqq~1e2JNX_j)`$IZQr(0fHrs^S(G}}l;pTt$kG=MmQXZ4xGT8IG zyqeM|YG>hECrmsSy{BKEc#S8OIqY4~o2RyHj(yFlF$Z2td>pWimm&t#D&-Zl9p~-^ zxejY{M(I-GzQ^!iG>j={H#`!ZB7RQm{U+io2Mq6t=$0AiJHF6M#?yII9bADWF z^EM7w)IQUAuUav$H2o|W>b`8sMRxT&rW)TILzB~^H{6)12HV>CtNI;L6yB_keDd1q zlaTz3;tw765=`ntm2?^Yi;IAFa93GmO~H>*2(Jj0zi&O~WZnA|IKC_+og;es?0}Dv%8KMWn`6MSJ_f*oIypCETq!|7W2qK)6-L59G^R z%nF^*t-~|tI+IVsj7`%SG`K-Stox_?tH~P_-_e`yOeso>F?G$`de67f{Uj$;OnW`F zD~u#*KDU51;zYSAyW%(*Lv6hFcVF5O&AgBrFmD)9^hBM!6WW`zBaVlw7wV`jXqsXx zon)KTJ?&%X^F>|Jntj~~K5FOvVe+5Gi5P$$&cMk8u4Y-R7p>gZjINP?_lA55tkpD) zF@(Y>$R~0*uFwr=b9Y>wAl~^0@fYr{D?$@AZbjDRMh7z5P_o|xu*g&oB`8U z2Se^S%+Z9^&3w9OtvDufJyndTBQTZ6qQ`*k!amh!g5?Z|Br+^(J~q~WR|y9-IJEb( zp!V_a`(a1d41#SXxoh3QVmUe(fwCm(kNT-*Xrl?CC&mPV(c&{YR`#6wqgFkl^efqC zx)cj1TkZpZ$i!m?{m9=ckg=~5UE_gyd7k%|c00 z?B;hh-A1{CXDSoml^FBb1L+1`q^Gj0ENUD>Fx(tj*T6IZHXNI+A6qiZTqKY=mchB& zbhmhPC$Q;o#CYXGW-rhM@ z81}-vu;Wc7QS@`H;~jGi698;KEm)tItsF4F1di*uRMxTx%TEiqBt6zwLZq|oh({N>nPFNM@Bz>0Uk7$zktE&1h^83=M@ zCU$HP_DMp2u$hrAIhw#ddXYOB_>5{=jqiNgv_hK z`p$La&}7!n5U;iqSSnaM~Mzf zEf5bL^%dpE%rz7xU*7KWY84SJEqi3fnHq`DKuE{?E%vtMs_W+t#La%}St`!?K< zK#jRVWcXPhKZ7s>_OMxNIt1oF!c%YpAAG2YU}R#5fGVI4q5`gUq6B`DU6bRQS@+e- z5hLIDx~)xOgO6M{n%FF9EjBc#RuN%)JU!N^>aycE49qFzc0v`Xp+;_fdJeu$g1eDD zw(n<7KU=C2o%Zo^{1Nh20>QU-_LBN#&qVfGI}mV8)ZCdE4Qdhj1ql4fUY%!;+BQ5A8^qQ=QuBdDLWL|+)fyd&OJyw#he$6 z*6pQBy3VFQ;in_T-~n!|ktl>rlg9v3jO1}`nkOx2s<*|-@@j)72KkSttq@voTbhPv zZXysWZhhf+PGOf)%3tf;7-*dIhJ|UHMvC;G5l{bEmGMAMtAx^tyM!~aQTx_pK1u)~ zve3Y-dy>ujR`R1q$rpW$v?*8%Glrllf=SUc{YeV%pE>2}49~{v`bsO&EO8|EMyB;5 zBYnc7+9wJ1M2>bp#lJjUs=9%+&p=&3U37oRV3qa#xeqf?kTfKS?u7hENx2Q}_2>OO zKyDICA&8Ue;7!%CO=C^Rc`*^YN_ODERi8O7tT7+D1&^=oo^P!8oI$yIFA;6c4$~(i)R|XCtj#x1HF9whk&1 z$N*(|cD&|6YQDqYrnlaX0o5&lU+;Ilh>B1Ba}5(x*{k(rJXIT&JuI-uew$xmJu91~ zL~S4Zqf0$BNP1D~5YLBz22G41|U4&p9>6lW421=Qh1naaZR|oyY zS3fuK#ipi`Gken!&M-^tzo^hnE789!QmDZ9Bav+%c;`)Y>J<48(VMczBFR&hu7_we zzRvy-T;C`uL@{!T(bE0 zO&WSwe%Tcg=~Gfj$i~w&ZKpB*6Y^h5n|_mtrM%EP7S)RMoPCWX#M#U*KY}=!LIaV8 z!cd@!?oj>WsaSry^{LNCRwJ1}BNGpch3(1EVE7HhixINcqd}_6Dv#8_YR=G$mx5gZ z2mVp8Aj`KBR49Fb4tB2JJWShm?8{nIqkajV+P|nhWVmpWB!dJ?C&gfY&1S<@clxy7&~wm z?R(E9qyFeW9yN{k;J1$sAxz=;p|@-0L?`@`&rM>F^UWve9rHe~9d{^F*E(RoG2pLo zY;)qI$47@)sLkbV7D*I?65$~hDPZv$3nsgjsWBS7c{Zri*VkfIFQ)@8KJYQ9*cBYt zHyw$o&^j3;XN2~1D<$0m^=Cpz0EuHt5o5K(3g$^+n5VK{;g$M0ZGM}kjslq4`Cn0? z4^B`B-8HV6`RAzf+?l7k^iR*m(pyHc10~Njt4ld1?B?!OcgKBd-WyZXi#tq6UOBLN zu|3%0d{i6T5cl_IKxc>4%-15v?~T)~8ZniuVZn61?{E0GBJ(-nb<^qh9C4I-reBF@ zEVk<4T9tWJmN&kEBN+zj!VXEp@mG96|+ezKNY3?Oew*4vHsAnV^UOKs;uL#80%AK0;NhCyu+3a_pSF8&2 z8I6JnHIGMf{0MMh2%u?X(~N?0(e{GXUWAA(`Ew3wE>1DD#hoOHNmHHem8IIIvhw)< zWIIB58&_-XtU=s=a}AFw#6`21#koqt$ z@_3ZOzvwqJ8$C1GOB#H?!M9=e1Z%SRjW}${f7Eo1ZU_t`jkhdYVgS73YKjE-53l4{ zMio6A>FwR3Vc47R;e}dnw$R+|=!7%7euPUzGrU$apa0R3E!ia4dp0hc|#dGR;-d??AG6Y*lh!u>&1;q-GN zc!EK|dZip1`gmR#Sdl45b#JVsa42Q;@t7+>RvP7a9yz zWSCV5Rco1h;$j9!tn{RQeW#h|jX)p_g{7+*Hj(?iZX_b00=?&jJCA2@gN4{FA+{&yI))lCpmk z+hGS;DlK6XMCNP82b5+PLNIHCngEX4J1g~n-}T-+v64q5wBRGA$W}C9S9YU1Ot@K8 zsv}nP_RB(*A^G70&ir@?GsdJL2S1GmP#MrN&&+p%&;aH#YGkG-h~YIRa}YM**9Q40 z!UN)ddF%UOhatNWnEc0YlW#9B$npai^jg#-6B&? z9QCLQe1x6Xn7n}v;r@^XqKaCG5Vj{kA}}CKzb;f9KnE-8M0Ex>Kj4D5A|SOf(_x+N zN_u`=0csMXc{4p_CXz2~&--6yhmXGojXLrAQCnr9*|Ss6k$2ltC}jN19v6PH_j zam+jF(yed>Gb1|k2;Z zZ4DYC+Z31JjYQGH?RYd3URFxKY6z$79R1Lar9v@{jdC=Du!N{0_{=&7c^SI!5FmvQ z?m7G~VKmcgWp9<*cOnzuT|#i>)5O0r6+Ao;mIFvO5-0f0AC&%;InB9^>Ra&#>BGp* zO(^eF&yafD;Qp|WA%=7=1qhm-1*!V&O9W9YzOpJqrqdk|vo%$g1vZ=hP#^bvcOHfJ zsy^I=*Exx8Lm-`jG+!n7^qa!vQF5v2{bvrz_&mp?!1dVKa2Aj_@kwsBEXT~}X2WLR znox~hn1JX0mrZnHIyD4LZoDW>pNL^2hYMT1&9KhhmsC~X{RJM(*Gj5xD^!C^D8yDA zT$i>)s&L*4SZ^LGqPZ{r!1#hvSb&UZL%Sd3t7X*}Wb!tDY%K1Bf|iu95AO+Vg8Ey^ z7}5?dZ!&92q zvbk|<1}Hr6$8mu_&uw>~I70VvtF>^e;({>a{R6n-#ubvcHQ#-j6mlMbwa#0|?>9SV zci+Dgn;jM+9W@x9nLb*irWwN>Qa;=nqkV%X1Tj7QxtE(k*#EtOs5hahD(yPc^J!f7 zIa_;JFe2nX4xbb<4Zppp-m@_ z4o;@vR+={0`I>-zTc7G^VsCXdV+Ih_S-QH zcDVmU;4_YE=A0>v5C3(T<;|_Wh3Cbm#Gcz+Y zV;g2_l7@MQnVFfH()A|RjOVg z($+F-O)d7aoIhH`s15~?DOqx}l>o2pe8zFxyV&`;M}5kAnwEV^bnwH$bpV9hhOMmI}sDE>OMYl`~AB+3BO!cY50@BCA%?uu-1)>Hv<`2)9OT=jrl z%n!l}wGEPUS3jGv3jG)ryZc}8jd@w0yHeQ1}4dfss6DOyg`1# zB<=Zkg3Tij(g(5J`Ag+*>ejU5LY&4~0IddG7XN2p4lO57@p^jzq${34-q^ZVKpBz! z_e+lMGr`Xvabz??58L)g>nDas#IJPwzADUYNVB30juboIx&gciZ)#FDu1~)=-wbm;B$#&7j<)q)C3jSli{D!wkAO9E~1I zOilG@Ac{0SK~0L39HSnF&F^L`7Z>ZwJtC_fi7WWe^R~)k5g;%DB3N^AJ^mh=hHvdq zmbqJ6%orZ)fk98Rd`q%u-+sD$QC;bu0?%n#(**AeK6REyzLys?)O`8*YbOK^0&!Xe zqYc(Pe5Ditrs*bP=d?!dlX~=>`?AqcnD(oF60K_xLeo>h<3dEz|H|!VUS)=vo1_JW z-9GB3$rN~1YeMr*#!dH!EH}D2O(DG4sw-sa5DtL5j4{ifCuBK7y!sK3D0>^XT}2C{ zNJe^ET&^WWgdmD*NnU4ChW!K9lOU4$YUD36ZuIeU0-Rsm5678|0v3}w+qVjh^WxQO z=NF-JQzk9qAJVbPTRgA8>=MD)HHdPu`n`(776kb!qrs#ntNPXt_z>N4Gmh!2 zs*{!RSv8M*HfH(!XWJqyz$MJLnjFeuRaG3ELHt(#E{A8=S&w7hH{g@FGMjnjtHG6* zj+LzkY5mWIINO1vpkfz+p*Yp=VBi1PxzFE(OrbOgyJ!{GI5!E&TO}L+(j7SaVM5*Z zffYXLTync^E(Ql89I&fKGTK(ZdZ@J$V^t_Snf=`}M8C%%n5N940vWKnE>yf!XURn1 zz%kxcIJ&%#YSJlDQVFLiZ1gH0Bc&@X?O~XpmwTa^Q0x)3fJ~UR7TTf$9KH-NT^@Lo zRb5zzo8h!;(4P7W*FF&B#xCAITD-sU&Yxe-#diAI2G0@HKJDn*JsD56C^D*aAox-y zZ3pkk*!NU3XDa6UPCoJ~ET5KIvOPz-4(AKtY|U$3-rCpx%xPx#)W=wVI7H}0Odtvk~ORmgVJNQc`sQ?>>;zZ@5kZ5^akxxl?^#@cpoYXR=)-ZQ} z`#s%Jh3g=4*GzM?9zO0);_(3MU&57x({+<4nI?%DGO8rNl9gQh(mmK z(}4C4-mOFS8CE_9-@I+-*uoT=YEW#dbc(TL=>b*Z2a*-Ok!fpK%gVuTzKry2boKg&+}3F~_nCr_!pdXWCyHT1+=705*iI9x=w zl>H;~w~Us2 z>)U%zvB%J9qSEQhRMZLDjYH0}>v!{}G6i!ADyb}^3-IAlhWC-^WnO%WDx}~`X*z9_ z);5wvG{xA9rtcs;MTcYoj#PdwBdyI~a-vQ8wHBMBnITO;p|C^`z_PGoocIz<;8-yO zk9Y*0fFlT(>!^ z**uKsR5EpGDVQO5tGT>sx8oMdetXvhWzl~&a5aeha8|HvVehi7idE zF{CYaC&w_2P0^HNudT1Fils5K1b$8bFi|hC?KQgO-V7m^9fj;UIH&`rT9w@t*_-H4 z2kfT{Jo1ktc~Y_|9xK@o|A4S!tkqZ@9QY>3=H(n_z|Z&WI0%i*AzZoGT#pZLqChNL zvtj0zDQA%(aR+hJ?U;%iv`#Z5@3AGVMCh((`{AH>$#OFjXLa~MQNB(pSl@R5KFynv z=zA9t0ST0$x8XdpzV zEc!D7PQ$@ZIEBwy(^%IIiY<(NqB*NCGxo`L;wfN7?>P1O5Z}IEeZ3VJ*$}cahXf>2 z8}A_CK^Z1s#$zIDo>B#hj@nVX;VO=-8W|gF&{D&p=M(sjfqIgJs5^HqE_|lSC{=_|d?&UUjC;CT#B}+?c0`!mr(XwC zEWP4yk^6GE)~vm=T5^0NF0xi1HUfo*o}Tm10QFeKn^q7ccK*C^|9jb3LNpQ5bUQx+ z*y@gq$FR;kwR2}};Z)^TYc6)~K?t&{wW+;t7RI=GC!W$B8?`!+z*f~5>&&GW*L{a- z9d5b!Y7z6C^r8{Xh4|(on#dkAq&JLh&kZCNVxD`T3syo%!Z(){dif4@d6U z^AkexyQ?jx!Mv;Xie-KF8{|%+01`M*jg5#7^NMgXgrr|(nZ5PB+69wPe9x;_8u_A& z2_4LKoHoF==O=pdA1L0iTonO8uye$=BJ5Ob9EX?PY2Bfs{mx_Dg>K~}%EIN-D=gqj z%I=C!UUkE2mZac{C~W2(lA0UBJ*vO9eaGkJ8{_*wEC<;(o*&MZMs(+s*WA*62$WAq zINv&9&$=BO{Sn^Zkr}(DH&~G?ytX2^7s<=F`*Fq8=j^ymXi`YMvIzyU0BXpdZ%zbD zk%zTZ{AWiamAQ$YG4(-=?u`cOb}Zi(nx|ClOuKa$Yt`4uqqlrqe{WBJ_zbPD$8~I2 zNa@>Ukj-3uSPQqh3)%^;^>cNWAF8KxYG2rnrUmdEpKYG}jA5=Zy4I(|GP|72ZRc=5 zoL8VIu4i7^b7Sq>tsWI z)Agj#;8)gt5u~cnqdjR(4kom2y!6v@Ab~l}vCfbh)x~o?@OADy_tqOsMbgyo(>)V9 zzUfTyh`sId8%TwdGHF7%5ZE0rDC#by!40^bHh#KEeH##o;hr=Kzt=B}t;ASmI56yR z+W5PMRqnjv8`J5;_Q))jotb+=PPE9124(yA%FQdP<#_(g<@&~0Al9H&H@DyIZQTj= zhIdD~halV4--C*V8+X>xwp7Qp9GlytFBT}x+2GPr)YP_P!cSkIfau*P=iF6vEI z)=Jv571X`qhiYFue3aw{Nq0=Qy4!JV`=$8OaURlE zr{SE>3WRy)ve>aihZN8wyd;bqDq2UqsG$e3VS0?vwr0@59&;O2^q{v3Oy%?rZeyY| zauM%Te`xQ`AF72RC)5ocH4ew}wU;6ASW9IHMH;=flk3F8(c`lySmM5jpzI0tqS&sg z#UgI~p(~`GmOM0TmqJ)qsMi$!wd)*oRK!rJAH+7*EVhM=q_M=i87uy6skyd(Hi901 z+pBBjqKWJxEc*Z;`~I=)t_5LK(EVe%9%in?*Y~Ztu`EI?OZewK?aZ|1EK2jQg&0GY zY}(OqyHh#)nn)8|XLZYo4wNL;mP~6~Dm``_ydC%>fnU|_>|c7M)zVE$I{sJOXFiI) z)Rt#GWg4}GnL%wlUE#PPMwR6oNh~L(IK-i!m#=Y295-FfW zVea%ug${d*?dDZ~_Y3ZD@dbOe?lqRgdIeQ<|cAiKFCamikpnZClg)T9tqHZC-ABMz$BkUO}tQNGZHy)wFdy z;UFr!HDkLytx<5FKgbGDoI5vc*f?A0xDNT7_jsBAQV5qGtoW#F)qy~cfO1G@&_n>V zT-uMbUDa(!4S2@+eR3Tto!dQWYreA4d>-eCq&oQ88!I{B9{dM#c3Gh*C*V=+{32tGq(h|}b znaI7?(L7{cT0JOCLMQlXa%o>O<>K4hqo6;4kT56Ph2}vZLNqy5%6Ks{;$ODS_Q<<) zDxa9!(O{4tG!oUsp5*p1)3nk8!jD@Mho;fp>YA22WGwVmx!9C&wv70+N8S@uUPL%t z)c2|Rxf+Y)PpM^qqw_p-?tUPTMHb)%YGb!3*a+_XdH2z{?%I(NubaM<=U4LE|9gQD zW8v=>-OR}EvP8omlwBdctFxZMQ5Cz#Jm;i}f=r?rJggn!=;f5tA$m4VnQn2U9V43I zJIoKqQ0&>8?U`IBe*rY&AzT`Uu+S_AKYALmAXJZsO0FBg`aom{)IH zf^?@Kp%{>g^VZqRqwwMr1sKQj6$;o|2QF=4SLT*qtm;QF+E-y)r>-* zEh`)Rx8q8?olL`Op(UmcuUtw*chVwcpP!l|{wN8jBbDE1DUPMNG9%F_n>REPB9kKBV*YLda`_S1sHwLnS%?EjAi%xx65$CFU zXo`RM_ zcHoqO_rUE=FBm~iB6n-Pi4&hDKm_8v>EhUTwYN`v`MO0t|B-L9B{a09vP!812yqcH zzA-NtaCEYU6c&q1Z#)#S6Oi^toZthq#l4l`iA_!Kk?pU}FtyH^?jE8O;#>!!o9LU| z4wu~wwB_o37ZImu{)xO}Hm_CCU9fTw*lH)0A0cZQZ?ObASN@hTxg5_W8IV}?zu z#z769=}Zk#R!iPiM(6T-8A`iBn#{cMFJE^uz}q{0_&}mEK>dN!w)e&P)5E3*Kvjc= zY!=kw!Hh@vrq87zQ?8Jw&}zR6o~(husn@&(aW`O5HAh^Z8E|8T^$I6OS%{YMc0lZg zO+8X=?YjXJ=b&KPlI8TtwlLOX7aQ;phW18KB>}dXZ}nX|E=p z?4Px56WlirPmh59mYqE38+M7EVeJSuYn?Dq>PSk`zh&(9A+~T~u^M*%>sqIwg-_`9 zePFjP<8>C{N6CmR{3k|V)4=}9k?-9g6}{#4ZLNQ&_xlyG zBplDCf5$BzWl-RF_S%eH>aCj!Foe;!W;9=D0LsfS@9GyPh-*HsmS(bBX&mC0Y?8X| zX9DEWbt_$0G#6Nw+q+>$>0F*hg(a|Kv%izX^*4#?nGEX)2}Y3$1HA}pRdphA5$TWi z-lUmOZPcKCs_#ymgJ9=GrS^^v4>TWUvU4&mb0u*hvt+<|r>D2oxE^($*ZM&`B+k5S zR!L8=j-W@|y7#K5{yp2C6bc(0m<`8uVU;L1=(+PhywrhxOO$)KGq z!x=L!_D%QtIUwZyl?3*r9#~^tIxbRy9~1H7kzY)|kqIQo(5k}_+}!bKAiAGx{9MIf zB_x)17#81^SSQ$~6LI5Oq2^|dsKxX^9E(L=!$ zcUV&k3x$EWzf=<4fONe-LdLOhz>{V=%f%>_lLbccywIKk*bMjr^v2;O2f(W+kIdc% z?1Zw3*f|PLAk;P_QlN^=hs2ARjtYc-w0Ph&SX_S)qdNhYp=1dyQ+CkaD+4|0**vRE zzwcR8J63FLBT<(AFn#;&dHvW}@y++wE75WQ^3fGtHPiq z@P}tk?%b`uOF`%rv$)Dn1z5W<~MM7Qhc~q7iBA8n` z)K}Jng@pyT%?s~CFSHOjK!qUvVkUCp z2W!dFUobJc{J-8jz6KJ_zaJoAv^Rd)9w+m~A$iOBv zL4m=p`MY6#-*P8nz@x`u+h4e%`=Pw-{O^<#@t!8K#{oQA-x}ijAy;8D>&heb+$oxA zx`@1{QB9ihrT@VEUO4(2puxMVq%jT(Axr4ArAG26AVSWjQiO=|!-ZQY{TetD3jDac zVZEO%j0`Z@%_Ar-@D zNzenb%86h7P_0qAA$*4R6TUoeecIyNw0jo8bpv%DOA$!fXj;(}$@FVzup)9-@M zl;;cD(HGZgw0*G0-EkiEMIjE7c{i`H{{{fjH{IomlugupjjD={?eV(I_+~Zg?+Wq; zDR>_B2TML$te6b1ab|P-IaBCE)&D*x)*$wnwl-h6s98`Xy1}t7YgC%Q%y0b#lDk=T z^M-~9MDu{c;c*bW<4j|!NtlHKi4)9t+WiF>wz@gxi?iubgqB?+OVV^YemLfSn$rJr zD(8{ob>oiOY&#W3i+Z`z;#q^*;@9Qh+I@18YEW=;SAHquNTf(IDIVG?4r6M!wDlkZ z+AXll4^fA)u6NXr%X^MToQ~hbda1#!|6|cpp^1MW%k$@gHxxX z1+;Ll6d_k(Uq3#ZZD6CmJ3Wzs<>%V>pxE`{0uDdh6Fe>LJ_u9#?@4bru8m(df@Ek~ z$|jE>dy@uVoL&}vBsb@6Q+V>aZRU tZ`a=}GRHHIMy%W#6jRZ6A8m->pIE=9hM@ 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 gj@*0Pv>S3adV77a1D;880WuUg8@eFkVN%n2v zR85U0)f=mKT6i>j9kcGT)Z zIqY+CXx1vP4`0^(7&x|cm-a0NkJ^I;;hve^?zjhB`f(mUJ*U~o{QDgje!AcrU1iR- z__I6&GynRNTDm1#;k5gqJ9SDS4tPoyrQZsp>%L>S#SEC&=7XM}1j&E2O#^VrJ%QK= z0D32gZ>G8(bd|No{-ire19LpXhJm=^FIL2DFqNcnB85u~yD=z!r99mMxU&P1@;3!Dctg%~j z^BAA4E<5tJe>0pw<0HaFvY#BVb?JAfH!l>(({6}{e&z0U^{AfL6V<>q2z=v+w1rj_ zS$#wOZWQ>raW_8*#c-GN)Q>XF&sL-CWl759X8T<3^uN5D1Gd+9w&Td+r(kP>`|I#D z>~kHOC_}Cks(n`cVE^mcKdvJ zNRZck17T~ARLDIf=1%G4$h!DH;@v-1xma2PI+yh*=Q10AxqqeFRBDOqK69;mmtZ)J zQP!`^q2J{4Z1`ys4Ey)Dah!WIr7VR)v!9&zM-qm8J)I&?gz~)SrZn@IvF7Af_Y;bA zUL|YJYARn1X~{AI(&6j29)!)-uVe)`grsioFXN8d2kc-&T6(ZBt3OJjm1Gp?G+w&B zvF+*hzrjSq!TcBTjp7Fn;JvffhbwUsn9;M@_V##Z(lgPhu&C6=S`lEf7)!SI#+nRZMtTTNcProSYWM3v_Ty$RO(WFF0h*jNNsH&I>lvr592^)$%H%UGYAa-G{S zq>L?W%y){g{%cnBCBmK+4H2S%PZo=|ZfhQs0N?r{gM-Hs;{JO?Rew~dO8Ay;vW295 zIiZ%saA#m6c!SC=K0jAw27(4c+IqHK3O2w&j^$6PO|L@By3nj_XJLG$(4+0Qw`gsa z#o=w?x=HIpumXY~vj1J&r$7FPcdhFm0Ft!DRurWfC30U#yAu4^;MiyX^F_NcYjS>v zv;w;Ehj#9GI4^#L<91CnD>Jl@I=unaU-<{{YDId;EZAL`G`QiJDq>~3&>)rRsd`v5 zo>~;L|Lf7I)2K2BV!6jrI@^EgL?T=s!V=+A?#y@3Wa?HD~zaZjrA3@;8X2KYtG zZ=Zi|;!;tZiP3G}T0r$rY#ms%U?mkf>pSg4xlmHyRDve|M4oa&`)+)p8rj!$wY%q- z-HyVegYM~h8y#^`LKeq_j}Wn+)(kxZF7WqzsK*R`i!6MD965j>HOl19N~!@a1V~4P zYF_H!e-e4iWzjV!jG*CR$D|ZiBW;cjz*Oncx07Trt>4-dzPQPv9O&0av=?Ej45w8j zR9qPmDlc(l+jsJ&IUP~>Oy6+zj+J%CC>dQ$%8N)OX3S|ik4_lQco)^V<+bJlCcNBw z)4P+U;z@puTQ$>gEFLFht;$y|Z_J4lEZql4{1Wb%P-4k>7)EOgdBm*XpW)Fw#KTN;x$|}X3a?Gsh;1F4ZnEJP878moi+*E zL;VMJC&8QYD2w2~F_nWq381BBC{_{KySc?2%zh))rkzKqg=Riw#)Wl8-*5N3NLLaG z1s>?n7i6`^ohfRU|1_C68D26c>ghkqCcF)bihkv_$yN026t?wZ=%H=H+lI2Vx z30E6GmP6e4BUJ}Us*F{FT=7hL8~(Z7;Q{G?*D@&XlcF%jgp$66d+O8r1LSIKQnWl< zzg%%4S7Aa%MDBl0*9*U}%1%X|7fHC+YTnGGjsH1$LG@HnfY!;~)&7T6k23wA#_l?- z8*Dulae!DEW-q55RF3e~=lM_kkvgGHxrHhieQ%>_^19>~3(^x>Qv<*d(<^2=FndSb zrlef|q@|y-^&-oHr|8kGa2gy8&_ER|6fyMr&-%I}1tCF>;QWbsY1Er|ykgd2W!rhn-X*jY|xExn7MbA@GZM1V;OTBp^V6->g zu)2zSuTw>VwK#&g993!m`X&C!p@(J^48ilD$r0u8XC)DWojpWMjS){b2_Z`g_`5cW zXRpvrf7~?vg(x})$8=)J;;H!-&ZtCg-}!!H`7?KR@+B9mciZ*+u;ZnETH8ZiyvV~* z9YC}k4syb*MzCN_VpV;bIZR$9Kowoa-WrhPL4LvPhI?o-EWE6>Bz@RU-gZx&oqJ{# zApMgOn-`Fv`4Zl}HnYB9ohL2mxROk<_s*}EPw{W5UU6NOk#VfvG@KvIcCoF=o{a3! zRU~})QPzR>a2|NU!B_H!s3Ku5lw2&a6OXb$*0a3`%V~9upv1OE&iUnhaXJZlaEY#5 zWUr&SGPpli??PeU z4J2q|7E-3u{ko2LK~;%o&B+i>45(4?D1eB8U_5h!ggL_&W!Pkp2!@$WJ&Ty3E#EUI zg!@U#G!rgz4z7NFGzb|W=o4^QBMgI3mC<|{Jp5nt!r(Q>sTx4SwYtW<(Q^O4ojffx zV>2&~54qzCJEC8NiE`9a%23yZVF>7MKu7r0gDGMtIL%M&xZ8p9x*p=>k#~kLGcG-0 zWt>^FIs{B`(o4yCuTs*F+Zp%&%!nX?1VO}cg3BZ;)3z}lNe5J5emP=Z7*}?W{Fo^G zCSxCw+fawY%eqbE%~*EFH6$hlg!Q@~HcX*yVczx=k0JLB@ML73Fr+Rq8&&G2&<2$4 zB%H>&Q$HS(B{O=}`#+$!AJ+GLP%SJ&Syfq3H)QA7G{-L`pq@ng zUi1VL0X~8lC~r}lQ+!*h9hgPGay*iI|46-?4&*$|2--rpSJ>#mP2X1rCnOpe8w#+S zzKgT14tb5pT2&N5WNjsgoPsH@V*ae|bGH;||Mcq~$Zjo9`mu(` zN8`AE%zect9=2!NT9z&b*pV8u11G+~f&gl8E4ij`V^b@PTdaSZ<8@PDXuZU%W(#4B z<2GI(r?PwkYZOb{p4-ubv1=7hxT3?Tk}4B`CG0CCc1u zGvc%%XMz9W#b}oo;FO%g;aGW2d`)meZ(e%h`zuL+(Ahyb08=;MB<+`P=D#R0vbbLO z3FY{ijIIBXU>P@Y)Np6VT4n!=Q#0Q}gqhDBO@;(`C1@|jDXkJ@AQ+`H(NK>98%7AL zHu{MB#6bgRG%GFLAst{~(|%X8PJ?a;&>0_lA3L6B{+=;8AG{(PfDuF7)xqRIW*sgaP<`)@Q=}b(q4?*f{iT4Qcp- zs#av{?Q&z+n=vJ`plM_&A{tLpdr1PZcKj&cIQXL3IaIMuP`&b(G;FQ)gK1ypzjKrF zL~Hqg#&OHm#LL3Rmx}J*lnn~~NB3C6R#M){_O!R^7XEAx;v^$j>SM%$bsxa^Y7_S} zvXrSn9N5?l#-i%M6kPC4kKN!|@BzjlDT;L54%?vwdiAdY!o67EPq|pbjfk>13@I~f zG|GrQxj@vnKovm1AkbFkCaE_u|AAgQR!CCiu$+ZB?So5iB{{kFJz7=3jCBno#^NpmJiJ z;mTNr&fjBb1c%nf&LNAw9EI&L;`;9(Nm6Bks2PD#&N#T4yU!mjq1#oCQJy4IO=~M} z)J%T(E2ySq&%sJBo9c&zBXU=(m&SDUvqSrNPY%kc#TsDkM}~4|vRwHiUZ2fT@0fOr zn1YN>@4H$@P2cK^GTHJiQeZ^(xleB;EWN#^W?F8wb+2tHntnh1blpf7Ry=!{TaPqq z`}3z{qyW#~!|p5G3n>$=i`BH!Gf}%+q47M<%IU(ctn+f3S#@7Tx=9WGe7(q%FcJ=z zthedwV3%EWopXvHUE9U_FAXJg6IV^u9)84Z7#ervI)TaqFUnpCN>Az9IU78L+QBJI zFQGZ!dP27ov8u!ec*(!oZ7Cv>pDHu%PEo??0jorQL|}4O)eCRHW+=gFE{#$xlez4+ zM|B*2oZyniLyua&TZDZ~5v|T)-bden`h}*SZVA)f7RJ^NVkqgc>Q8wFyH8Ku&k2*JHNaV0aUbjo=EWTY4u8 zNW2Iq<}-d!p6+kw-yp|>V_j-N0tjMOPRAhGTw3Ut^kWOmem^ADTS2%ONTaAF4BY$K z6%#u{DHT{ZgXr@q1+!MUc(biVLsIOZO#to~D6KT%E0XST3iX-m+H=6+3!gOmd z4Y(UGp{S`P52qh*r>fwt`}OD(cVziBgwRZ>L&pBM24yqDPEZh{FP^8+Nz4=X z%D1a&n~j>T=;1tA#t9Q?$Vbr8(Z%L!GZi)lBK{FItsk*x-TA+Xsgr)2b?8`XSgvbY z!^Gb4vrG8ai(NKzVl?#R#xVU<`aIej?%89A_pCVY*wnWKYRIgwQR`S0M^FHN!QW;V z|GR>8ygFOKbKoaf}lH^C$Rj_Mcgj&ks7-Gf!b1vAY@4?3^)C{`_d|&I|iD7 zGL%QT#a|Fqk9NR@^FjwQs}j!4srFb$stV}!h7+rvkJJJj#p4!y!PGQy2 zoIUqVRC`xu+g{HfzDyOg4_sY2nVs&r5j1)rNRme6L-P<0 z$bU>5diVZ3$<-KBeX%JhMOwa{90>P%x|~6=f1foIl~h|6n=vQoIev40CnxXE6Y_~0 z2PSX%tw$|y=MzlC9(i#de;5+>`~@0tm~GU_Ty7EfRiIdhII+wNxQIc6c1zpjUe1)* zi>E`{7}JxE35;`%mUZBWJDFCJPNliaGtk^+;QRvvHe4Q_lgn^#2U)61Vx+gLQ60eQwXjZs=gULN98q=Ht<{Cu|EP&?|>C4xLn5B zM|LT$8qMYj5 z_g|q&^ld4*2VLv!PB#sue+&SE9TU77B7paf(~XK+m{~sE4*b|J+$>FQbN2Fj(=G#w z68#KllRwSx8Ayw#32{IB^iggA5h{ERugBAe#ZJ(!`_JjmQ|7F_Pj`w=Vw56t*@Kl+ zSSe2fmILpwcv>+9?JL(fKEfuX(6tTY&2Zb0Dt~+ZdOmi;twY6vWMf`b0&l#N$A|d| zfQs(DXsu?Qey~OYj6ZQXb@h5;-Df(rk)2767gJDU!l%Ki<^nUzpZw($VD)LE!GG~| zN0lqhspI?Ef=WEsY^#WDlh6pQE0P_8pnLBS-wvzfhW_M~nA~*Apf9DlnSy_gEXKwR zbB}fa6)EbP8aI5hv*WGwce|5wuMz(O=-iGT+z`fDY}WEtA61fXY&&!|WV6SImHZ%; zE?=md5D`f~b8%}VchV^3^?~V>;UX1@p!$rj5|&%VImGQDOEM`A;EfPQc_#b5l%Ul& zt456KadSDNVMZOI)XW~x%{`Mzjt9P}sQ85gBi8bJ^$$}erxq+r@xvOqc)`slz`%m~ zn9LzcQ7gp5&Z~kKG%8Q!EOQ&3ygm>K zvQ4`ns(Joi9OuM0mx~TYHn9C@5_@;!)po2!PP@QkeVpf1UwRVixQ@5Sex6!lY;DFDLN%jR}y(WtTpD1AcU<3ynMQ9V@lYrVZWKdnx z><@XLw;9)H{Sg=d7qYXKHL>h#WVCvchae9c z5rAyuA29}KyO!u0k4IjLf7GHpK&uQ_a{{cDcnEwGCy^8-Da8iVk*aF@4WWF8GmM-~)T`h1_yjXCEby+UP2A+8y_ep&R?I zXZ;KTuuhq-L+V>xA*NokTeNHUac*GRBUu_8E6{@Pt8PX{mtBx z4%wVTcGtJBG@;J9C_l+z9daYOWbbZ)p%16aQYh-KhBB4MZp5H!Fgpi-X^kq%9;gKO;o9`CYjclC1)mCBp}A8>08j>)t#T47+alOBj=Sc-Fi83i2dKdj`E2QHQoEu zww10GkG1O5R!tZ7XQ&xb%nqTV0drFa?6@q^r$pQJs1}BvqpmsCu@Ir4Y>aSrjue;e zM5LS*sT}4|-gRl`W~k16KUToh24xk#SsHioi4+Hr7P}7GUJ@x6=G^!v$D0SY`?7|2 z-ezSPou)`y7Amq|Hwe+ojY4dy-p$vG$h%-;mI{E|m)F&sF^QqqEDXr6>U5j^x>=Tl zLUuT4k<43HLoelRLq@|CE4G?;O+Du`6NKC7tCRN271!Q9Q=O5;*g%F|wsR;e_wGxss{LMmkat|+#&%YK0v^+?Y|3evEEKfJ3kRDT zjP-SQsyYxLk+mFE?bkw&E(4-0rU<=$4ISiSKtyc4^HO8GF!9TKrREi14UY_c_q29( z`&b|^`3de-ld^Y`p4xRFUCzc19qe8S*K1${T5gT$ula`dZbV$A8Yb^+0w*=YuNh- zRCY{Xe@Ewto4TFrf3S{vAc9%v*RV(!7zv~683J^b;b;Im=lzVFHG?Xbou08o0`!2r zZ}R#yR7YRLo*Q$Ti{x-4r?fzXGxs$ZiSF2-e0vVCqp%W7(f&ppAn%Hn8Hd=+2 zfs8N$C>(zfW9q#&N!0<@2)K5*uzYWn2<94wQCp(D{ZCoW|Ixl1;-R4U_QBf|fvy3=(TiZ;*@g`A8^^AP;e!dNg zRV3=Rem)H3n~+u(%um=g6M4RBb4?~;+eYP_2Z|iInx4c|Q_Ofisb89$`H%M7OS=cN zCdI)J*V3?%X=CN96{CfhbzoBOVa6s86W+OaM{k{u{}A_`yjKwzZpOIO&t!Y1lj~-J zne}drXQ_033xEdw^5t$z-~6HUR%;r5)AZl()sDC_m6`5QrWL|z$4htVUugSk=toPe zxG6AaP+|q;U8T@~Sq*L~xrO#{dWX|_|4w7YZ|^Tc^$AP5_kHz(H;+1Z29!S(OhjJW zGUaEd2g+(#A$pl?^__USkTv8dars18s{?DvgP)U)inpuzf0Wzs2&6%#WVa4@vqHbr zbb(N`gl4+k3}wqZ4jU*R^c8Ljls>EP>J*vR8D;dwvydvcvOlJ!UDV622=Id;#lKkR z+}tAq^ofmJ(#p!2d^Kx}8ID!M@TM`i%X5PabCc2MoL(rm6A*n(u)zuOauhmMWs1G> zA#b&TBzA_|=U44-CSgk!<0gK7lxK3-ZG>^C7-wwbsvlVjw{`?ZohQhantwUYk&W=AAPp)Nt2k^zES=6KocL^ zL9N1RL918P-T$W7B^GqR-s~36XI?X;JCh{41*4pIwj~8v&7Oh&x0jX13`0;toQ3SU q;}q_Xa3Nt5|G$pY|9u-LElS2P=CM5z<@n!|I%5M1{W@K@*#7~>Tkl~2 literal 36709 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

    c&f3%p#b~2pKfqw;#ta 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 b2@oY6iIof8J=kFS_xg{vp;n!Sea!y>g$75o diff --git a/data/fonts/wqyMicroHei6.png b/data/fonts/wqyMicroHei6.png deleted file mode 100644 index 8d7a44f7ff645a49f9f17f404076b59b6f3a9493..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4768 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 diff --git a/data/gfx/snow.xml b/data/gfx/snow.xml index bd3eb7395..e34c24ffb 100644 --- a/data/gfx/snow.xml +++ b/data/gfx/snow.xml @@ -9,7 +9,7 @@ y="-0.01" z="0.0" /> - + - + + diff --git a/data/gui/custom_video_settings.stkgui b/data/gui/custom_video_settings.stkgui index 466036696..f697282ef 100644 --- a/data/gui/custom_video_settings.stkgui +++ b/data/gui/custom_video_settings.stkgui @@ -1,26 +1,70 @@ -

    +
    - +
    - + -
    + + + +
    + + + +
    - + +
    + + + +
    + + + +
    + +
    + + + +
    + +
    + + +
    - + + +
    + + +
    + +
    - +
    - - + +
    diff --git a/data/gui/help3.stkgui b/data/gui/help3.stkgui index 89007307774c22e761d0acbdb169d190f88676fb..4b5c30c84313e291979ecb46f67a0eaf02ff3e2d 100644 GIT binary patch delta 96 zcmexoKgD5#l}vCtLpp;3Lk2@BLmop3gB6hN%22^jz>vd`&rrmW3gnjn=|qNPhHQpn jFfSiSrZHp!)u#Yii9nS(Kso~`k^+RO40)T&W!jkm4mK2W delta 74 zcmbQ@@XvmOm5gm7Loq`MLn=cNg94CD2SNpg42DvMJfNr*knP5h31p`LS&0leK&Zgr O3S>d`Z0?n5X9fU+P7p)@ diff --git a/data/gui/main.stkgui b/data/gui/main.stkgui index aba01222f..7a297aee9 100644 --- a/data/gui/main.stkgui +++ b/data/gui/main.stkgui @@ -4,7 +4,7 @@

    $s5#@YX) z=`5q7>fSa!z>w12DJd=8-O>Wm-6h=&4blRFASFn5cZW!KHwZX%=kT8AUF*Nr`8XeD z_UyCw9oKdLs8NJy(S`S(NSM-eek=UCLKs}me$(Tygacod@;gM5k*=S+i{sglP=9!@ zaV1ndBmR7K=1&|Vs3!)GQ6&P>Ss0Jy8Uu_W1v+gvsYDN>+Xt6EoJa`%_#>x zIH3G=lm<#{^=mFT*)L*H$#6nky1U~4_}-ZxC~aqW;iPGHWlyx@@2Ay_e6dB zb`JKF*V}c033LD;E#Zat2G zBMHUe%r*n%Ge~`9cl$Xvv1Xs;M`KVZJC5x+l?OR;gj6rF_?7Y!4d?r$MR#DYXx?_n zJrP7Wj!U$2P22^d42R8pSvld;A1{jjnPj+#^IhdzG!IbcU|1(W{KOj~X!Y6QWvIF_ zR25q(^$TL^7eSvK34CuRbcAp-UDON?w8;-P5(9E#A>o5wmwSq=92{9sdb7DPV$WtI zANOs6Zj+2g>Ul|}tdNk9eP(E6ns3A!2&F`)>H2FCL9_8-o@8ZD>B=Yr`(NbKZmQu# zihgAim|Mr)%bCgD>6SlbQ+%R@!>>@M-Q6Xf(ybPt!3M21`Fz^Vo8=k?!o*2kh@n#P zq|C`4m3)V&M06AZoXkd*JB^!Z#xu zTn+Lta#a2-0%JbpBZcJX&6Xl4;nCb_ybJ=F8^{tn2=N!Mc_mF@_(AL2xB2V#>VdwW zmSz-K5r%M04GRv2P&RrWxNfUdz&m~{>3HeizUhn^9W2@iS|?B=4f)?XQHX#{TY9l( zK-3?odACHYMnat6aY`5sT^i4;Ht#Wj<=w4x@h5{_Pr@9HG%XTRf7{eE{dEJL-&Xa% zt=(H5{$}}4tnLW-1eSvLpHT0!LVGYFKt%ZUV_pPAHQNWAw?qv1>#?F7y6A z>{lOfH32~qL1;|sJMpQe#ZO3uFfUVm`WPx~m=-DQP4pbGZqy-GcO4hn<>9h3imN8TC0;= zPPH}mOMLoMsgpiOijN*Nw^#fWelK=lcfiKlTAh_}+~U9Dnw^oJo&=DS15!U6!y15| z?cCFd9pcD9{rDYH4j00A>4E@hqMNc|m=E-L4eHo$gPwoq2!N{&e1UF1i3OcypBd65 z)e)W*v(k*2Jco%xo(C8uPP{ZR;J1pBQ*-gh@%nVhrSW5##O_*&TX)a|25FfRw&UaS zE-%vy>aB2JU3W2ToArJ6HvExcM^Zw~H56byXC5Ftu^@-Z13*c7uP%MFfK7&Na3<2z z=Jl({P5SH4W91vN->Km5{Da$~Mu+#njhDg& zJYqebh9v?@*{I5KBb2IRB&G1K3CRKK!+VmS@q5#76OyPusO!_9ONE<-TbIX3<(pnI zfUH*%DQ4s~EZrCkTx)g8Zy*|LtRBE&MaMN%nHB)Idcp_nmF@m04F6b)2??yEfez>} z$W}piTbnu$1B7{qw07)qw}llP-Tb^Zx$Vxs23dTbQ_Ej=-06OXomG=vT>$5-A*KfT z?qQRY&n|!~F71ANhq4(Z%!d>V)wvh;qHcB|s>8zrx)uFu`Uo{(Wc+FXh-|6sElz_X)(XRns&$rNZawcD;ZXzz zvamI4sUBhI@t$XQO-p}W2xDJv#C%>ub%l`{U^GyZz)Pv()=aX`Pv#e}QkZ)wvz z%_GzePHhcgc}bBG{R60SAs4 z-85+%tNrk0J$UHeE`qY>yFd<*0k{P9lbsXgKJPtx2eY%au`|fY+{vwSqod(Sx%wvBrWmL-r`u;xIrhINLOV30Zt!Tp2G1 zxKQ@pNHF0AX6QEWzRy_y9EH__n zWI*9!ES%YXw4;#oZbr1F=)QBI!~!iXpA!e;`t5@^G+V9g9oGevLC>GBjANG_&$^fg zK9z2K_QM|J`bLH^wze>IU5WOq6VnTD@vX*wWhmqo1ktO;4DgX+;3ac=@p5yU{|p%! zQPdjWNo}4z%24RZp>(`5J&rHeul%XU`ZEJgL>#$#p|Fsq3JES0+LBZiCh*Nw6Io*J zBn-!f1@X{3offfwhe@t{IdoS+AOQk+ZE}zoC#h9XMS;%l9wnb6X7=%g;u_%?DSW^Z zh1PA!XD|5M!Tp0e>fh?;4wd5)FEZ4m@ZcH9RB+?DI6b^mKtunsI6A>Ix!v`hArW3d z0nplz?40krj3LB)PV2*ZUFc1q(p`E`qV5ii2O{VyW#$M#vkDPg^u&?H0h|Qm6OLhf z9fZERz(~cSUrUskqt)X*KGV$+Gv%g2qrP+%u38brT#lnz}tZW6!4_TnUiSW zl^DKy*L5vJbP^=()^+VL3dbbiV>rJ^X#7Y;ewqtD?#)>no^%`Gga0!RuZpo$vK&@7 zL^_FXy4JH`c3=-O+1gl!SZahUM#5bef{Lh2)k$m6Pz0Es)6KP3LevffTnvvO( zyLD$DG9y{k_yqPaGwYqRK8c`^H>0({gecIJMd^vPJRV>XK1oQ#r`h$CVdjaWVaI4q zH_$E~hto*RrITF4MKriE1Oy*?|2ZfO-u+|Lu1-lc{4V&h_+nWtn6zp)Z!YY+$V)=S z8J$6?sg%Q9=7%{-hwXfMeC`5vFPLTARS@~P{F5sMIzRd372tk=0NJhAWzY??vow&q z0dC2Xd3);<|C}f`gC^>fk*yKNTpmGjG7+V;pCBEN0;GtF$DT11*YcX+Y}w=mhH$7M&#rB#@l1x{cSki z&q{NfpER#05T^ov^~_835NrnvrK;!26SOQJ<@tW|-X?QtL*?J_(8Ah*d3b}?;S&e+ zO2Yw74v^uGjx*%6&x!0<5-NWhLjjgEBC&IORY z=1WJRV^4(sbiDRT>-kI$mW)#<(e~R6a|MSaDO4$Dm+9fC^5Yrk67=AMW~LSozuQGn zA4)i|X|v=o{mKK(N5>(Q$LMsE z&zv4PoE>kMg1c@UXgIbXe-eS`c%D6{s9**O5b0plyse&h3Wve#7pY__`)Q0*w!;}F z`hmEIMQ_>4!%IDoD5Tg^B!y8p-JAQy)+K_nfONZvnj)A9t0$p|IKVpK3qOC3d+#($ zy^&z%{_qlmoOdO-im=}-_+PvF(8eu3)=4Lyp@Id%N ziI}w~H$q-7@LiXk0{I~TPE%<{|BXK-X_Vs2ECiI%WK>nlUH3FXTzur*{3lIZ(z}G z`o{g|F}pX@!~%hU4P=|GeK_{!ez`XPG>Y!yP?}yw0jf@_lydmFeU_ zO=7r2YSi^x1-luX7|u9sIh-HV3T?>!U>oHk@Gk2ZVvTOSijD<0nS^I&0`{ICCHW$O zlt8GoZxwR=#j3?OfL*=-edw!2fA7DzOpkc~)4|o6hyNzVt;%aN@8E5<)nVr?bOz#H zdUhe1@GH3YHtIF+RPDPeMH)Pc_M+)NsQ(*$lF_gk`tn+d!+qXqp0bqry$tg{Z=gv+ z;{q?-8#HXl?&EJl#)a;DthFxwV2E9-2fq1uf0KH`jB1ByLb?A=9ECy1I?cckJ_?b%o7_&t5agq!!m0|s!SzZJn&?J2AO7ZY zW58U7z3$Wb{&A8SG3Cp#m-pmNcq*ns3bhg~WKX(PM|SHPRTp{yx1<&7xpJK{fvB!u z05~O}#MH9-j9>@Yl$VU@qUgIcrNlCpV2N*VR0>Xo7y<~Sz}{H}6?6SYJ)}9nga^_4 ztp?L0k-ebFH)0l0d9Wdoh+_1x+*_0pXa9wV-A#)E7H9uBVZeHo?EYicJ;6me5-5QJ zGMpPzgn*ha*rV%Q&GfyfhPWWwUwI9osv%;(3>8iw9zQ(Liqb$o;9W0%R8)uSQR}0BA>`n}1})fttz>hrn^E;?j)^{( zmlR{oZIsPt9csMD#>k lyGJB)I3&sIRA|cL;|fi1xRR-z9R*c+Bb43_p`e(m{5u zu}qs5j}Xae;BzWCHDS$FelIdJ#D_XM2^UNq*N;Eoj>`y=dfWB^*K2(f^TNrKz+}4Bhm~v7exrcIjz|P5 z`0Br^RwA(ab$|=d)g0Ji&&4@Uf?NDa14?ut!W9)AWqWHCw(RAIPl%w zmGfjF3ZFA(>3!Jh6@2vXIhpY`Rxo zSxU3fI&jLJ!}XbYSWz2`lN+7yOJ|akR5e%XC2eTHLyU~Iocv*`?eTyR*WAsmuDClq z-cYZe`#Yzl8o}S|-YQJa(|Hc`vPMFc^seR|@{8~63ef{;PW`mDIom`}c-l{nMXCmA ztQMC>BMJvk52!b;ViY3{dKe9+`3F38)#{GseACx!FDZ(`^|B8!)3txTeE$3^S;J$+ ziCoYPtZVpL7HfQ+Pf70HsRE+BK@Ru6%ZjXlf=hv!hC*@bEe{U;PhnLe+qdzY3Am1s z!v<1?_4ptSumrP{1eQD+1V5BoDqmfSMmJA|flh$v-`Rr2+3)4o&|2zspH&`I4@ZTN1wwSxl=xU-l!Z1004OQJTQY?lg z6nvb#5W&)?Z%dIjk!N_Q*6g25mXcBm@gWf8frA_NoKirvt`F=NSE8fK5EhJQ%ng`v zR1skcF9&yQbE)O&vEF*LUdjO?U!eR9wBnZWNo081zU3g(&?_EEC`9pSX3HwgfaHrl zf+UOgg%*tv4_THNC_EK0`yDc6$mK^)C1t9hXd4f$;68LFGIB>A(nvF_W(98NnwXE- zAJF-ATT?2&ZdyCK*j!-4yBZQ13=Qju@Cm1@>NE6xi}}-Yz`?etLMbe$K@$CU9{Rzf z4^)VnVt^F;G;i-2ZYLV(*s%6qqwNhPa>+d2(2hc@Z42EkP9)8@Z%}nKCc?|#2k!4C zG?dKp+rr~gxIiA=2u%3ONz7PVOSKj)hd*O=Jhm?$t>uC7C0y^{rwEjJuw6xt6jB*D zrn=-D{4xwi9saCfM{iC*hc10-NR+eN=f3Wta(`6G<-J}W_8=GOom2w4kRdn{IP|~` z|0@30Uwq=^grd8fwuc7C8^xZETCteGIOnp|9HE*K$&l*r!|DOL@SS7VX5-&J`13%l z_K8)#DY$cU%4-2KD>~J_-77CgvXVwl&ug%d8N3^Yf3~zn9H)|NG#eg4>X&ynU4&)I zLvY$davCILBwGrkdiqtxsz1Y>3>cBR4kGMlp0*yveo~W12dDk9V&!1p+3F2z*$RCF z{64%Sq4ha|?gIrcz%+1(vh+pDkiLdrM3usa)7cz(1m%=dsgbW=AX(MIhQ{(teafu# ztSAIhL*E;IgfwR9_WPd1R&&p${^znSHM%s;aP9Z>iIWy|M~^|Ql$n@u4AUPW(^|~? zN)Y)UJK1RZDbpy?&ZN{x*)Fx%fU9)%{-KkUgIRSOeZh3C__?F_i1YUlh({>fQ~|$) zCY=1SMfBx6QNSzCJ15P&?dpOtXt){Ac?Z+KqAwccSR#xn!lyo>7($FFDeeuL=b}_* zjQPs|vdlLXp!66U4y34w*`=1BU~eM9`AWoRL$7>9RHL45K*#J$Z`%|&n>nKa-T96R zVxBf2fywLrW2m@iCXisGi4#2wn#DO(*mD=OtQVAFK~hw&pT@6MQjh>VBkEDps?HG& zM?Dqg)(0@7RFC)GBV7qa7-WID8d*O!6F>t=xwj6nA7Kj++wBI5q4Qxx@X871?d#SB zO{C(KK1SeL&;>%GXssWQV#7b^d8E6~zx%Q@X4<~mUB=IjY3?9XYiGFk?)|8$lEl>M z9EF(lhDRW|uP7FFcBnvk3@|Els($%(nlWa*Yt)2KT(0M?T+1b+%t%)mjXD#qIwway z2!@aG7}iyy`_iVgB5MCJaMU}63&KOp=$v!30~hll+VX1*10D7Zhm;*&={FP1jykt(hh#EOV2LCW4f1PmJECP zi82wpvm$*ti}l#>Kvy&Bc(xjT077%{ za?F{kIaOxxaY{z9CHL>MNL1MOsIlMcDCjk+=y|bT??FFPr7D}^IMMQX>DxdoBOj0L zI-j@emRXg4X--Z}0S;%M92qSf7o(U8 zL6e~N3!UDeBDx!rd1rDj=?^%JTaH2VAlr>`wQ}P_e zWrj0$Mn~}wjyz|vgF6ZSQ~kQz?7GL9+qmTUl9#XNO1iatV6ycP`<|yv#UT8K`#WMB z(E*V_cSq=G7@O3`89Wp*apB?ESmnd>*tX+g@>(b@?7>DHIb@1YOg0l~Q1|X`Qv69! z0jxlp*6$$0{t8PEe%Z&^W%dn=SSrXKuUU7Tx?9Ikx*jRe5($1}7K7dI;#mKp?2@VY z^lL?~-_Gc`8U`;(EMt*!1%VJwg<<- zpF!iybX&Hx`Wc)^DH^f_D~ri+>sw*b1gT$#Q)u+7&WBNW`EA~z zv|}Pi$zy{8y=OtJi3xwY0v@lKH-cYZzD|_E3hfc?-)AA#A6z}y3MK8BXFfT6$un=Y zZ_pe#*}6jEl&9?a-pen9a~P5p;yCU;wc+hLk?y6BOFfX$$Y1Ca+B z(@RYM5Ni|nm~-`;1fAR59|S-6(jH$^z@8_&!UlJ&vtN$tCoe{dOBY@$UAo?XKda_9 zcwXgzQBd~@yNC}pZ@&JuTsfb8g~e*#`oDI1Gul6j!uCyqQPnV!k=+Ac!^E@J%Ee!n zIi{i@;!mgEu0B5APW@^*B7-)Q-ApH~MvE4{K3C-A*&=;|%Ws}9)4KJi1al{V@+m&CLR+Uo;p_g0H!bazH7fbWj^==U?QR8TXs=ZP34|S+@LVd`!-W z!$qWg%m=59$%i|@4Pu8yeCQm5#1ID;$ZT{PSNddTTlFrx0;u6?_f&9J-36t>76fFRUlb*~_7vB+`VI~i!Z83B;tl2(2`mxf zw}A>Qme~%C!fpT40(dR-_@9T6=DB2pZrR}o9VZljYBq_l zSmFqJp-RbvyUIJh9x0wY2mZ;sv_!P;hgU=g&A2!@qEF5!($~$&Ag%5moeyR@gg08HE(Qp~-J}{drzaTj zaR~SQ(V|jR?cc{ST&-16u2`OShIn|W{{S{F!xg=?GGDdZeXNZa_#g_$On+1DSB!w2 zC>w)m3|`Z9@#ZXZFLmFYHwe~<@RHc=&siS4SlRbF3-6QV90_xKQ^xlZ4FEKp;hYA) z`l0U$eCe@@%*reZxeAxdd}MqxMA0B@RCvQX z*0$RlDJ0&;QSTd8WH)`cewaXikW5m%JTj-JjaGiQX>}H$(R3--Sj-1<5(a?Y174j& zfI?15a7u`?eV7e#!#EK)tg5QYnHV$z#kY9-%@RCwZiFPoYU4Dux2G!wcbWq~_x}9) zd}x5wVRbHNi6;nbzeH%lOa1LUZzsm9qm~z5$ml+}>)%Ci2lVc5Rr9OC>BF<#885RpJ90`$?ul?IlN9%;J4oW*FVd`sYZqEl-T=Hh^#?sso0Vdj8>(>q^?j9e6 zr4NhAa#bm_&D&K2AXsAec>=N+ca6J~d`~6GQ{O>ok0)O3IPn6q12kdJZ%upp`3g*Ukx0e)-C`!4?UlYPMEV7YY!S9G>O^uka|wxf z2aiN#aS{qfDn^F!#3h-`Egbt(o7s~qBL^NvCLPfz1=EjB{AMDyuZ}(Gzu*Q@P{JXv zUdj5uvf{o@t`K&Ow;JV)Ch2@bTPk>G1gmb#k`W93oUj>q*|3&N1=~Vs7#~M?*gLwy-+8}%eh#7vIBQR-zH{lmAFs@Y@c&Hj zr-$uy!_r4`Uq*vp_d7-R92d+zJEiwBenzoi+#k;}`dsdfV9dW;&-%CZ*u@cL5Ff5e zR42F4gv;+#Ue8~G?(vfcQ)CY%Z{zi^tJqs4V0Ngof>zSlwxEp6LcwCAy6kHszHPFm zXr5o*>**QHxsb9Vwvz0_C+*jBtXB}o=T)sMtaf~d>RgU?*mk8Qx2JqL$-#AksAYB+6}uls7<@y(1G zUM7oCVC(wHc@~y<4*SI*WQaFnw8u^P$(7Q5a}U?_ zQ0RWJMcCC>fE_vLKzNfGK3@r?ZnvxA^At-)HhH03p{-RY%3DDza~!jQjH=Ghr7EjF zxg?=H?9<|Jk&^zw9C_ZQu$#ugC1|R7>Kjtt+`T)jT|N1Nl)3Oak9!+vg01Nw8nsrQ zZRvVsI&|ZQLECp^;IjEhvanc=>*Nub7z}{sRK;A{p?}_vXyC=z-;Aa{o`zHZu9Tr7 z)eK>=c{zRnB8-;jFfPy^wc0e#xNS*1Dc;AxbxkRL0bihVoI&cM;7;gZxirdD z@smPRYj!IaVG@Nq&l%WVlIql*odxd+I&!RFLITz_4`$%0j$`I07PuK=?7$X$QyCOe zDIQsgcIV=aCpm;}N@HOv3=psY%C>WNqB`XtQ7AzBl(B2-o1~ZK6+J2)0#S0IuU8kt3(*4yQuVBvHMY@?5PkB8=@WkH_sb%h zFX&i=-ju|dSC+ExR0rLZ&sk14{gU&CMmdv;1VJlB$~(KY-Xkv0?aG1GBVbyLr%gOP z%XOJyE~^IB`@rn(s*CD?rj;hy_YSd$;sSaBrHcrVR+GH%-2mi#pWJ7dAq@JfI?39Shwg*8e8P_xyI1EUUghj z{Js}UZsS?*(Q(GifTmtt7gcnV2bGu+Yw>OJ)QNS}O~AX@Flq8!)>{2)$(%YbDf=A1 zt?fMG{IksR!XyK1DYg7F_xDLu`;usWhl8c+p?6*6J{Tc$J$n0jLU)S?074Z@{F&4D zwRxkBg4a|6lTwj@F_SoImPa#qyWEp`lukp{S^)C z%&^S+U=sUL-Gi)aY?o(p!k;4XisNy{5{Y)q`pN*=RLD(G=%$&zR$M62WWjY~$%iZ?2TmI|W^36PN zQ`QFt&QF972#HAbB?Xp``I>~D#t`>}V--m0V$E)Eg;Wv?(z2c8?&TQkV(5W@uyFs? z0rPdh^Mvp{3*{b)rnRCfc|h{ZIDe$CKpE2-vg4}f(n%Y~4GDYoYiD@Fx-)-e-nD2TQ6+%<(r6P_aso&$=fsw^U)^2_&W?MI&5(Ov{zhZ=ro5);O z8NL*MrO?CX2WXN* z8s4}`>Fs(^Cpp8O5)!>Bb4`6~dKNEgPy7P-Mz(`_3oorKtE&6MqFtX4!3P}1TO%oq zKCdr#@NMRB=>#w}Tfr$`#lqoAD$VlQ z&^U9)rzWgXxJ=tu3EV;lQRmtn0!<`EMW4MCLNUykT*84bTBk=F`d>&3q$9mIH#gb# zuhq-4mOB;%u-FcAa78(sz51yw!r!avv;2%n(q}HQ1gh+vXW1Qu=lfytE`VYh76^<6 zmkC>}%gYICa_R-Ov8Sl($BNe_a!_3h@)q|x)MW^951H?9cUp!nD)<3Dk3~e z%_P@2w-+N%z3!t#xAaL%>B6B?${%Y>LNZ`|mrMJhYeYN|KW2 z&a#(H1ca$Sql*h@?7vxOPB7Jp~*)SpBe0Bb>) zU_quduQ%TI?tY@66!qgNBej(=jf%8?S0mSHhgJhLh)yX$Gy_R7w&6{7e`MG#)AF+1 zcCa)Nlahbw`r6{iMZ?V@kkeaONbPZwW?Ax1UPD%~91WOx^_dRX4qs_H?W0l(et_>GNuDIxPS9Q+<3AtD3Y z;_U%BVXW-K7T z#lfY%|3($GUund@$;+RA2qV@MJny{QXrC50^?h|(O(3r3wP+ewk-+AzH2%UvE(q_- z{}W`uy%H!CddB$1c@A<6i*L>5(tu-%;zPx$x9FYockro}p&7s6#n&Xv`z!aw&z|XK zHW!93LAvBqV+f-}2gS=uGCKEFZfuf*%He#Xh6m@-h;+-V#s0O%;v|Vr^t<%-ToD0f zrmKT{K<3Z?53Pc#e7?-1zbrCuAF1%vwlfe|MX)Mf2)T<*>`TAV%v+Z}P>?@p0k;_74%E z!G~Cm^SzN2ZejIuUB0%D#-j>1`j&HODig&SIfg7JaQJk}n|I8J(bh`dl;|u4@wXst z(>DtkCSlZ(3a18qR09(THNlAl4i!xK#NTq^ng5-vG_k!A&f+*fy!IF%4ER0#{Ub)1 zWKX;EeP94c3(0X6NnZX*_$6aE5dC@74NYOe&By5X`t&8}Ql5#8mA0iz$$xgoD0c9t z0B=0f-1p@YMZxzG0@DylWsC(`?HU=A-Ft!fpnqNl0GFGbl5#!6><&9o<6Q}z7E%WziXK^L z8Ci{8=Fcc~@hWzX^Wz5_U-swp;0k_J-Q|;}YDB#_t{pN!DNSgStN{}e)zMwN-z&g^ z10_<5vK;hj2HyCWh5urPl%X1k#_dnzNTD~h87m%oMRP@agU?D!Tz)NjLlUCr_xgX;13ONh9 zYybZu>XA!u>_b38*IA9;*z~=Z%fyX76`dmI+Bm$L;`ekOh_f2q*c}Iz1kK^)^G;*u z0{_O-Li*{ud+EzZqfN6;{oUNx)z{dmJAg4F{=AJf3Y#C%^-^ge4cEt);i?7D@neMU z|GYxGX#Od_g$KLs{D_jk7Sss)^C|xc>9v|14qK*Snz1yssnwk$5<)HYFopJhcJ5j$ zi|$|4rnl?K7%u_l%ZOS~Xl(2oLx9Ed_zw+Kvn*0kacgfS!yYh?`CJ#K%%O;Y-fdIA zyt8bR`=Lt(1H3vuSX75Ye!UF&I5@-8#(V_1Of+Vqkh2C+K;X%A!^;j<|;J zIn7s6Bo34aFAF47Gbwe2K7|fo0tf^s|1ZMm>z2G;6Z<>KN_C0-VYkCAV8U;F! z2AvlQ37(i`_b4c$Qa-JHbM^e-4s3vgp5C8J!nui$;MAwrl-y_V>wV&D^y~fpkJvOL%mVwv1 zo-r2$NRjT0p2s(zKTJ0Ri0Hxc;)E_v`?z{YGw&gnoS~7C8CR~=W#S&8eO4^uG>_+LtW5nzX5O^cM9rV(b&?167>1Ugq+y1z#T~lQ3~# zD?me<-Y+&#ED1(+zpQo-ioeW?Z$H!*`@oKm!ZxpEG2jz0J32&Qo9Rfz*ylUP=tMR) zK3DhszR&X}F9fhlj@LTKf)Kk3RuMt_M`slmTES7csrUIko7U~pi%~N0;C!sWKVtRj zzf&ug!9FQj@Bydj7h7h!9 z+BX%+@z(xQ)pCq3zjk>Ow~VuTlRpJ5W0e0iTxinNxz?1nYBxL4dTEF;dP3|H>%Dvi zwOK5pJRnz9RXsm(2_^%8-TRL>Y9}Gn&<1GqNf3VP5Y$a4>3;#yLOigXre$K(exfd`WV z_Zik^(38{ijmhf`)5jS_?k{1-$TnQ0vb@^1k^Sa31dCA53l+cUk4~RVs_imqx4ZOM zzi^pa2V76fx3sh{ovZ*+qwGJW&1OEB4rvfyRvK>=vQ-(8sF)bLk>`^uj)VH#=cT{g zow-64T4$w{Gbdic+#(Bl%TRGq(LuLX@-!$ep{E9B8?1`=Pb1Ep!`!5Ed;(0B>eD0f z`zx$GCJ1MT^ujxB0$_IJHS$+*y|ISit6wklhH%2S;Y7!4l`W!&x@B6fB2A+rqKdx% z1cbBxqfvg5>E{q(#ge&`$F;=Lc%kTWX38fVG=HN4hlUs4E{@XV)-c0}JdoBRG_cm(`6_IzYn@h!RiH-~ z7cLdspa6v3afPkAgBVCvvdREG%|beH)Op*THfo|KH7{FZn*Ra$dg>p;e0*C^KK_uJy9t{|vLvI})qcf7=H-!>C3X@tL9!%Bf zcH3YD&hoUhwXxs~wxBu*kxm_~*h<{Z0~XV5#U|cPO7(KPf|ur^4!*j%gJn)bl4`HS zWd|K;uEu73R!3`h5IDi`_9N}mgU9*zX+Ec+ft3(!ut*1sYi|R&Re2UeiyM9oM9LCH zw!xa~I?S%iiXa%;i^A8S^#+jX>Q`7SdPczgo|jNy<6jU=*W@@cYnDNefBR2e3sGtK zGiy;+$Z4$?KcM>jS#Cc>K}oqYT_E2A5YK>#8C-6jhb39wt%eCR%$+c7sgZ5ZufjgXWtUF*hK?g^fm5gP?%p0aDk=&{Y2P6W4(a{fw&VGkWmEvp+F33cM{7TP zvR_|=IVw5nx;xYMO;1lV?KDu=I}KK1#eFb4rZHh9lpV35-pD&vBg<#@G@?I&qUidY z@DP+0gzOp4&dFP6x2fz0Ow3E$cX7c53`gI4EbpDVF*auqK{D*gGQ@2&% zWzBxfW3%M5Zb8`X^V)rRs7Ny>lpNX*vStxJ}eqnX`^X(d* zHs zu@#-yH6b1Ru(^Rq*}m%Tzt&H@ zXDf9VR~06w?B$-FlS3O00W=pHwpH6L`wAsFzzSU0UM)vUy#tX_96~E~-m#{S)*U%m z27c*mT|H5g|5#+|u>T z9g_T&=4hmBpAhuDJ@o4mo$cSj@$Vx#7)1l0C(4@}a%XY@F4(ggxv|im8zGiMK&7gT zUsJu;?lSjDbh57r1oc~4oNjD{7DW~&1liP+03i|_j$w5bH7<^{JcT?t&znp3UkKz{ zE90Mp#=iZdDnT#F`QT!HszrGpLhWX1rf?{KSk=jOo40(JC&uGR=}6T6_l=&5NmQUG zTHWf29AzJS2kr6~VcV+v0;(4j@yF>zXCM3zCiZ7$Bg_kW$s|7cIkP($O^()z>|O|; zupF-(o;LKogn^&~DK8;)c7Z7uRcx4Ehg|h)Xp*PV#wEKwq5TPuz3JqsZhI6I*PFsuB_yIT#nQS*yQYp5 z+Y=zMhY(Y$p0HBlBb2;W7Q~L=ge!tVIa4enVlu5ha+K(sQhe9GgSSPsZqzqxUQLTH zAX^h#M2lJ>GbIY7Do}CJrjXT3^m23i?*#Z0?@8hKNlQGj(g-S9-)J-HQerc-ia{mC zbaTJ%9L1=X6X&LUqithjGx&6PC-!vw9^fq#NhSV;HK_p;FXPjQIO zMzWkYPo&9nN5F-xe-gk8&yr=O5&UB0pLDWGU|3F)^K?!dPdi7ml%Gxdk=cLBT%Pyb z<}Pe~b0NNyyh2k@f4WW1KQC;L(YVvD_3}308 z%}#*y1|Bx;J8Nl1)4AX8MrXUnG-$tG)R;@>&BE{c1^AfT zrza(M{r*a27Ed2*GSdp0GD*61?SC|Cs;X!q(!uf}Wz5@nmHw+91vv=C$e%>RItf4I z&t;U<)*f~{yNaHFqp+pR1jHqnp(HDvO>42`xgNCDXQKFzC%nlX^Tg z>#k~b`@+Wt>MYUwKQ`)B-K0po3Xgs(*9ELnqQCu!9cs}G7OOnVUU5p7WR(pzuB8BYca&^aKTVM38#jB zXPY)J!{z+AP@A=~ao=WWp%vCPqJS~%$k!iB#No_SxHRsUmgekOY2;5{)Yth~q+&-g zXJmWz5P-k&&bBCLU_zrIzrY;vpV=HS(4yJh-Q5{vjn;FY;vHyOij+NLJGT~3Ie}z7 zcC_8Dlvp5I+|=uFxR?x=sV~yYkOj_5TR~s6$UF_)Dsmlqb$ML@>Uyrc7O4?`XV5Q6 zgF&j;GvIYjZ&=u2^U@@gINyK+sALSV_^W7IJ4|_W+aJ=aJXV%JoT7r&8S(<@4n!6D ztFcS5M0?;vq z%0G2i-%|xpUGB@V3U@Gda7QU^p)tiuZdyj3O#}LP;}uDcM)nPhk}`+~HBn2UQJ!rY z#kGX>GiMGak?R01yI4R(gjHI`6T@CQ^{;cuZ4oj#O}Iq<*&shNqH9%%w_#!LUF|!Q ziK9+k0#n0VbtPKv{|oXd4b})KS-LpM8FLB4r^+VwLUlvdzf;$JHE-92zr4IGhqG=Y zt?`DM1f!nEme!_>fetRMU(1GhH7UoI+icEnI~?`ESgyH-!^+;Xngx1@&d$z8>PWhY zlY>C^{iGaoS&cH02pn_(N+PhN75R%y;jYEZAh*6XR59qV=Sde3-;FcruNzv zFR7y}O&e=5nA)&d$5Ru{w9`?eRNYVe0OT6AxBJH8M)v)-+bzz|&vA5ggztUtdpJKo zFEcKtA`!j*&`A*Ro}#sT^4k zmV#`G(Ca$b@LiF1Jpj5c`h7iwo6V*)7u5nCw%ykrAT_|YX~r5>#THIaPm2-0yu2(2 zL4hxuo7V?P!O6N5xR3H}D+4t^H)Z7QeY5F;ve&EkQKKYl z)IR_Gb3A+YtQakebJzi)B7zW;>MsbG&009;MdtvdgP(ag!iAE0TdVYIs{&XOkRJX} za__w_!5^Y;5>Bb>q|^SS6Mk#n+8igGshLQ}YdKiKyD5*JVnZLTH{gRp+mb zR-JE7weIetDqC8r9?Cj3s0C82H?r?-k^CC1S1dt2pQa7wf%ZzzD*+FjiQuSiT~@n%us(Jy7)qs6`#D5vseuZj(RXm-|{{ zq+W8$(p%va`@N}1uJ3!yX0svyqWQlqU)Bc7HX2#X!1fi`XlBab?L*o3H5k=K)G`(K z8J8)-S^arz*{)`UPO%7YBl@WRz?5;Y7{wGPrX~Z~{o4rjuoj+7A1ajvqcFuhF1!XMtaqQ{{nt0v=KD6}(gUjbq~>rI3{pT!b5)C) zE1IrWx2`};QIqxND=txcCmX)qMnHO5g=1fhS|fs&%>=NRgRY0BE_NL)rlL&4+g`JN zU*#3(>uHq%NK#=oz1sJ=>U&!Qi}pILceSs(k94CTqyOAMLhY3lDYqlat(URJr@r3W zJL)FW-?3#u8)nuOj@t7~RTFBDw6F6~-_sr@HMW)`U|X=Ytyn$z6i{YC#JaD+60a1e zaL!?~*^DMZ@52J>ipEqbHAN`0K<1Q(uTvvi_N?j7`%1J{UAhMOlr32|I8708Y(=D2 zW46F+?NB-2ZxKMeOQ}C)>_Z~@J|wVRxXAN8U<5FhULJohSv+zM%z(>NP1)q{`@*|@ zVX*y~BGgk=gF02Plve$o-YDDYR_DH2>btETJf-ql15a9#(+YvM?b_;qEx+j|)U?C$ z`uC*_wV{}zNZT~XmcMM-vgo2?CD%>|h9l(*%%`*LQD45qXb z?3z!BUQFfCbyTAURgJNJ*17~YVZ*^2*nRioNn^O^~H43&$ zQb&SQsiOsnZ8xHgP}TWNmH(I-ZaxDk=?)?gkPZjY9)Wb|2F2h(XyPG2!1CvyM?SPz zc0~qUfJ_ngG{xn&8%je{t-b1(mH3)sHBC(ovIGyU6zl_g)Jw0|>k-?lKK!>)nCn>w z>h&$d(5fBT;gHrl*&4r{glr>)x>;l@cdi9HExoy}+1t;tFS4;%fhA1o`|A6tfmyHD zc=6%|PEJlnJ?^S9d>fgj=ET=!xhbP)%dJ*W)J9bb{_1_ULq6(*UmNtP!8P_C*eXd) zcei1GEt+B@F6->{I*PGqS^!M34@11I(T;P(Jd~L!YfI>m>W5JUCcAM55|D-?X|GbY zL;{zq0AgAf>puTl+(8#$-;jf;-fcU;!g_kG^V{d>X0Qgw=4JcHL$#IG_p(G58#-HF zyY)O%%rZT^_3#e8Cc8PT#=hN*wsdFllqvRHZ3J|kGc%sbU23Qwe}j7 zF1rt7usBAInAf0N{h8Ic*w>m;HQEQ3)*x>kiPlC!8H*|=qI=acSMC0__d=u7+PK=N zRPX=c@&E+L>UxF1DZEb6RRD+(V-F5JL}F5vb9AGe{+g=*RDp~`S718$s$oWm%%xwz zdPKWeHUhYI>k=gCt!=1%AM} z0BZHe`EldZMKCiQDUsqPiN}Ub+COi}KXpC0&OhzVsez-Z@?CA=mEks}17Al07PPBp z&eP z=InJPXbL#ohap(w{<5BUTUNC9xaLsU!*BP9rD)e_!_-JId#^MdRHI-cE^EBD+btY{ z(wPud1_g+cfHZTW1d}uG|InhK6N^qRwJfwvn^j6X@!r#0kKaYx%<%NHr=#}MT93~H zGgg-q;F%qbwV4$*5MaH=0)2}0-R=Ot9u_qP4?_MY2=s=u=@TCg6} zBJWeG)3!RW4_K|M1~q_e)5F?m>fyD$BzBE!q0{;^O!XIx`w}_<5e^pfQeMsZy#xV) zQs|!shVJi%e60q+vJDe2nTlm10K%Y%2;X=tj}P&=~8iRUS= zWoOaV8)jdFj`fQ7S+J?{+f-Up8>p%Jdc7I!&8vweQzg=UUb+4}mZfINF;gM$KElqv zaQ!wkUz=RCUaqAss~}_}!Np=xdPY=0e^Jr0ve0YWR2|aqi5C(t{QZkq7F}a=Bb);-5^ruTvJU% z))?KTUwa?lz;;be^*8JrykQNEZK$eQcJ<+|$xu@x$!dH?54etSvjAevgniru)&dC@uXw8ODZGaxa7^ z%v^?m4$dNv2X{0Wf@;3OYfk})Zc>4}+za5FMkGdTHXH1=8#+EbEyF=oPftI9E`nWP zW#`qzA6xI8Le}>oyw;mqlzWOuW80DI8$Jcf)%x>Oo@MG_)rhZdfT@~$-DgmTk1s>Y z>!`(k4jYC~QC#b0ry9Jn>72cXHUhF^z^opkwhr5fLB&c{smPXgZJBLK<;P+G`W|(~ zM7_Aahdqqeu-3*-?~`7K8nvk&2s`}N*8J6RX(U`5g((o5C3OOFWNi);5iX5f`Nw&} zymAA+3PBLl{#yn=v@+22H2OFFmJuX8U^@eDjz6QMP7%)ZBYyLo;w@rKY)FnK=MP6xYL8rPJts+ltuxx#N%7E16B%90DQOiD1(IUyVJZO!gRk*3w z&Gu^4weH%mObuYIyWI5eTDh^x%~RNn*6*z_>6CDc-ak7H*hY_ao!#p39UdMQ!B2HK z_8Kk4+Zsx%&rlnBivj3oQ<*jU`!*U<5>?G32q7eeEh0FdjjYkxPy^r<7vQ85@SyF$ zbpen$Q)!uTkfpyu3?LA?uE*+njjQDqPESuq-FQ0brp{+=hu)Nj+c!#i3RkGP*Se{v z1}D@W|F-;ls!UWjcT9nOwwf^2K3XH!H8?eOW9{ayYtJuZbWTxir`ntCby*zdHY}rF zUrV&Id9Fi)Ie$7bXH54S<0}c zA}ST7yu}dY>wXSy09*+tj`Pb^ERjZ|lu(5oUYcYW3C7 z+1F7}t~L^NKe{~_HQTUOSe|OAvbcjKmsn9#yMgQG7prnJ1s+Zv6uXWV-M5F<9#*YD z*yv8_+1u?F%jNP0Ex3+!>_M+1F-^1TN=x7OWz_mqufjf-qK#55sjWWr_D@Z1YDc3F zogX;$zTP<*{_P6|*!w0`{?#Ck^Z$L)Kzk*F?gs*5P-kstGtB3Uk{7WE+SXIswh0tu zmJkA-fA(3DE*8dOen1cw76yY$=#mExrNtnK2$LX7NhekzeA6aE1Q7s+tff zf#AI-$xa9#ruFek1#mvG+TDq5jomc9>E z4p`;{r1_C)J_KctMPL#@oFlYNGcZbt(Bv*tW|0^IZPsg$%SuTc^XE#hla_;$D28_# zH3vdufQUkf;>g1{4Q5)gO1L@5In3rW#MoyFJ4w?t#KI5-^qdEI2d4x#VMGubM`)bG ztZ5+*Wo=99L^+*`NvUT(pBKGMuLs|B`?YOj#>BbA=F^`G8}SwgVJ40&DYa$veTTm5 z(8mB4F;%&WsdtYEfbJFpj5Y4a%(#nz5n@QaKsf~3?K&_s0fM$^A(A#3fzmW#L_}uv zeMf*u(|AHsLXeV2GUw(8p_t|(jj#v^g9S(j!Bwmx?JO z42}ZV_{?f#gg*CJvlQqyqG@0E8>vCekYQW}PgQoF`u_qxf7!k8bl;r%J&mguG4SlhZ#t?<%C3Y^mcmk1!^MDsh z5yESb1>YAsi~zu)EqE{+ZPS*?K)bG!wr$9J1!&UBc<+(#Lh?8Q5ao>^LLu~${@nr~ z5ted3k}Zf3hQt^M*&8`WS%oamNOcYZlBAU`$s>Yfx7kt>S&X^a$ct|L$eaKd0nNMv zIVVst>sL~l>BCkRTL)s9j|islI9oELHg4+jAxdsE6Z zSE1|^h=^1WlZ#|H@>~jnBPU`~M@J+ArfheU-{ADJlmu+@S~()}NcK{|c|D4y(-V+A zKBW*dMGZv4!U!=6xnz``b8x;PNH4-bWXYMcKv*a{4M`GT$l-hPEHOqRA_)Ny5%SKb zMRo)ZYCJvKSqpMOyx$B~7_T-QR3_ZKR6oo6OeDrAA#{kbPqM|a@|fRJ+}%tDewLFaOC380zH;gB`_I7Jqc z7EF-!O39H(Qdbag5->>}j1HlXrNL=r@IYvCioh%qV@wy}9L4k}AP@&4@?Jtn;HUuL zJjqprC3%VLusD0wbYKa{VWM;B`yiZC3^-D^Cw++I9K`2Q>?}+Y898o95ludIsB9Gr ziwKd36LKD&9Kete!q7#=tR*D@Va%>eW8jO&mpr@@>d2E*NQM=$4rfuO=F*zU1l=?bwasxyHVPRtZdBI;&OFC;5ozjUEXZ2j(kBEe01LwT} zo>I6iAeKSJ3dz_e-i@Yjx;SRQF>W&rK?E; zbOeIPA}m6}442=#^^M4g%p_n*p?=#mlA|RwO$r)P=0K9arq1vygMi}~GXJ##A)-@* zNg%?~bv;q8VJcE78Mp|MA^B#AND8qjZ=56}fP8ba)s|r&XAh8uk%-YLbck6QAsqxK zV5Ssi6Fm8pV-2x<-q*=Ps5cKW0Hfz<4j2&v=THkNo^+HAcDmV)()JP2yfC>@&gGjN zCsgGDfF+WKxZb-Gf@XbHjRT4VG@?M5KoW^LCL5wp&rUgi?4-@CchBVzJpxUe!96gA z7$i5Fr1u?4OX8dfH?-*_MdE2Ki3E`Jl#J&@VnO~)L2=keg_Ipm^9Hcl&vTp&AA);Wd0-{K`$gFM9hk%{~#mGrfE2nYJxn#IR z65f;|G)d70z%kzyN#r1oAx-H{>4;QdpiD&cv@ZqNmj*BdEF=to2Z?eKr>5T^qNUs+ zIc+J?Y|0UH{FPoHjS-`!X@p$D2U2v%#XxY*FaaW803EGLWMHx|pt6rGvTB2FdcFyi zqgW7;c$Zd-74*u_$WgF{pok%WDH6F9ilu?5l<*A^zm42WYBPl@YNNu9%W=N}>4+0a5Bw6dkBxeD0W&?^gyrgN< z)HFG@Y!D)YdjXRIF{Wgi>>y<%xTGAKzf%eslit3NG_E0X1VkVzh9N0ZbVS4{f>ED9 zB0wKvInc~PsY+5{g1mJELLiPo^xgxMBLdPChEih>LvlJmj!4rRPVzs4&WEy32PY-W zQDO|j%p${pfPG&#Qv2o>0I>XG0%wE?#FV0KW{%7;idutwWO4-Qa?f%U0Vhn42O^Hk zsJ&!srl`4t10xe1m{m{K~B`PQ@9Y?x<2>2SrM zwcN5vB}=mA@HYd4F#^QWwhfVYC2SU#J0w%+Dap`DCUXpjUV*xt=M7-0bHW*mh|K9T z@o)efhC!^R^%@|T4y9m{0<4C_Hx0<;o&%(0)G9NVCL&Qn2}2?y#2is+h?$$tIi~c= zd5@+U#9NZRkU^@-K`iL!GQOZQBoj6f(8qvAQjI_l1m#KtkPfELbN9$O@Mbp{`)nk+ zcweNd0n!G`+EoQ`lZ>$Iy$yX=qArcxautcvx~Ldy!Wq1G0wU`AlsYQm^2(!m%zLd@g}6lnx(q*@oPJwkrCop}&S^+HOn)IC5h3 z22w7c&o44U?-(Mku%xtnmJ9+Ra+DAP0gzVftUNZ1luH88g=e?-jB#5xx^%6N!-**yox|R)gt`ejh>YW=7Pzs4PFi*LB z(%L7osBQ+~Tq@L%I{j3UFN~e-4UJAl$@yO-UuY-Wm^|2(=u2gMu ztJ9%qaaI53Mu=NtnF5Q+KR>YrSt12zqi=4FRJzfc4aB-Jy5 zh=dSA?E9XRhw-V5lsz9MJlKSjIsYP`EU_t*5hVmYFCYzhqShN7oFhv6GL;ULkHCs_p zrD+yE_k25)y`CmxU3@f&kXq zzK}^fxNs0iC>Dc8=@xW|AxPwqi7?KTf)&_L5oWqaxkBK*D(ht<2Lk1)K*m;_IB;5? z5`w#IYzrVtAO)8>msw-vlJewhQOV3)8l=M27YJgAnOlMxKWU>0!%g4g#? zJb)OZc<0gh1|UM;ZxBL=A>|oC1c8A*pVL?wfvISKY6D=ODCR|aX6~GG9e|yP?7FV& znx+X&({S6gLPV~QT{&nex64Ei9u8F6+<9?w$WAeNJZQF=0T)oNbCOHs7S0|ilU7pd zoyaedW6B80nKsY}fuLzJnL-2!piGlioYM|0O<|bq{6mnk&|;~N=kJT2Kgm;(6B)<~ zmLwFJMez`aC`rRzfgmNBSWy>BVRXuq$w4GiA2Z z$U|PPDGo-Y0%R;8i(dk8PEq=SJK?mM^sBB})VT>t^Cl*Qh z)%ySd4SGpLK~xYTL?l7EW$qpd+b)Z9AcTm}hZM1@i_a0OrfFh+Ux`);AIapm3{#1t z62WBy437C?b5xM^Kn&BD)B*5z2kt|NUDx$7#vP07M5Ggu4nS=LrZB#}5rBIB0H6b~ z%U@d&-m>s!x7%%JZM$`chr3xjn+q7sj)+>3!WbhsN(?@hR3pu`S~zlG_6hij4CEU; z>SijT9l+$^Tq<=H)O^+!W?19A%$iw7eq-`=l5-~K;K*ms5+sLz0!=FvqRAn=_g;MC zA;Qv!0Ouo!oTBuSJ-funaL$W&jAm@U0gVhGCP@ZW2e1VvY`)#*=d{akZF5 zOYRzmM~>psfllO_%q&#n?i!!xLqrG|ZPP&fP*6$#wE~z4s!P>?+}cX%@M|;7qMelx zJTgPrL$s=s%4@|aF|$TA;>Z*Erlf8Tq17BiAzKYi5ycUqzu9Z z91|L!I0{tKQ%ItLsPV~>P6qF0ZDIqm2=aYS=?xqL2gJ||O9YXZgM$N_w==9)Yl$3^ zHE~Xu1q~YbMBF0ZHt>x@vuk!S#NBSY+i{FrK(+*I&3UzkU<%+T%LAZ22B6O`wh>@0 zg0+aOV~m?!*KKyY?R?kmX7l;%U@>11vrr#`gcy={Geb^N&IpJrZu%8c#aNQ)k!hw^W|ZXi7=xE!(2m( zd|cxT%PdvtMBP>KQkKP-o|yhW8aA183Q9FgDu{Ns*7++M+IoSgY!*p6?TwbARN=) zk4($O0;i8p5o4guCTjXh9J6>z(^A58dfDy5cD-J&x7+PH^!-|x)0fB^z$TyBegX>^ zl?R(W=%CI2H3HZ`WG%q8u&kJP-F4k!wOTdaH{Q`BEDjb6L84bCPm@B`0dYnOgL$~o z1`9}KwOqzhrk3VXih*;CS(2Q}YirESriHR}jhU8Ojg~F>**TZ$@flx?EFdHF$fBWc zI#@9k7KR2HrO<|$YNe48C2`kNyC)!V;MI$hGDA@-89Dz{2A-iJ45IJMnpP>^zz`%r zD>bP2g8YL3-*_~>9U6RgJH!}ZfiKA+vMx%D5yEb$rWmxG#9=7q&Laz42n5raTz`=n$4d5@UVKm~kDIP2p6`|C_ zQ7R`HICTbe6&AqRfg6fzWvrk{z5S^w6cG9_P=SdH^;<|==KFG8-p>l?tU!Gs`@~Ry zU?Ct%Zg$IT#Oy8eZARiu49ZV5xLG^)&STDfTv1=%`E_6%od~{ z!hcx_^EESH09X)Fv)gt4`g#TDeC%Ub#u(>X0`|TkSMXNNc4S0Z^xgxx>7i-blshMV zL9j3y?-Qb)OMlu8RmxXJ1Fp$=GvyqQB+pXUz6BygH&B^Lbjol7RmvnFjzjNv(`251 zD&6&=hX83XYCCu}Rz8AHp=n8FV%lYiuI-A^n9b&;%Q0j&qBhyIvssaOX35=d!iDad zJ}*Yxtkm4&Fq z^rcT-kWhQ?Jp9aI$nYgqfi!CH1mEa1Th29RH@~Ut|NY!jJ~^CJKmCK;UgvfM~;i04o3sVZIC@EWP(LJ?N=HE{6Gb z-p%LpdE2z!JMSSWD$=l0*;0Cd1yqSNhTO50$uSYRBs5SJH0*MoO0RTcOQo`jT59`2 zNIhe&%@|E#-Ef&CpStnJq-bNvP-e7isWa)LQSNsrR0aTJnQ{V^uE<)< zGIbEp;FU7d#D<;fp+wT8*ajYMHcR8#okL&-?{?U%*U6wV8xJHHs*yw7FMH@BuSf4?rcw^#D!D&k zOr@?{myKMSZT2YSv1Fh~#x2<_xr&m@t1(1K)N7Mb0W0O9eqeS=C=p6D9^Xd8=pq{L zMa#V@jS`N`-1lAR`>@+}yY+6jTZIrVLkQ>0d=_K8$d36Mz*HJAd1t4Zf$9iA4ZsY* zj78?eJkR$buP66i*X_33?dI@sId9up>&P_(c;9$(sbj_Dp)jH75D^qfBRz~U0);<- zEH%z0=WW}p`&K%}Tsd%x9^VwhFwC%0?zx*zpRHw=JWg_{kxl=5KHQXBVpH_Mg$(4p zhu`hAmODtG(WuX8NMX!Dq1**z@0e9k4k4!Z$_FBbki;mm4nV2N%ixtToR#vkl3GoS z!>R>02P$)FX(l^DEW}b~Et7AIz=n)81BNJMBcICP(;K0* zbs0w6sR0n9bvYP1F*5hVuGwtX@K;x+dK%1Z6h=|WO zo&j{symij4ImTt*bqfIV{9H#wE}{Rfq~N9N6c{b+z+}9s+(T1@=82&V`ug7B042AQ z!8TK67{+>gG}#ns)o9wRSR<0vWFp^q&OSw%`aP#qbVP2xFN_A9g$S%yYugs>ETvLi z*P-ur*{TGCi64eR~xp%qJR^y9&?tq8Z~=~c3;J* z_qwrucAd>YQiVbXVU_$b)8Ws3DN#gv5$Qm*g~$ewbprixoxT2*IsBVF6@a~WaO(R6 zz-!o_Q-C>u0|18rmibHnelU1{niEl@k;Bk{CwEdF+GhZ6l`E@s;qUXicQ*($8KvC5 ziRJEsOZ(QM3Rm|TlG{G7>`A5Q4)>s5-&@z!s0zL88t-!W?{qt+&-UFvf90w7Mgn)e zUz2M#Z2%@dfb8kV=`+JmQ}RuYzE}CrwK@En`-h1X;7$f07j8?LR1Lu_e=YKt9{RcY zbDoWbw})T9?rH??`?&8=#5a#OkFVk5hM-5LN7tbI&VYPs47Mihuc^SVBnm2~KVS+Y zP?q4<7=||g>D2(p&{RiX$`#(j^1gYzdHlgzfMFxs^Jkm#x_s_B^R+YI>+cf!-^T#d zLIP|6!ag5*tq^$gr!!H+gC71jk2jD1Y<6HO4Zu*Dvow7h>Q7OBahnmi>w&Zf*_pJU z{&MC|s=nSBfH#jfk5@AQb$Q-K1oo@u{J&He@U92a9$>2Q0ovp7-Jain^LX=k^SHYK zfJp_WA_A-YGuiO-It;)*Lr@!psh{J`;eYdZ^LS+=08A@&%Prns?t7qLOnz vH;;#k2I$Kk`mfT0y?MNOym@?$&(i-7;RQo+DWcF=00000NkvXXu0mjf1<_6e literal 0 HcmV?d00001 diff --git a/data/gui/online/menu_create_server_hover.png b/data/gui/online/menu_create_server_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..85ef1adf4ab1eab46a6addbd8d0826977ba35cc2 GIT binary patch literal 67543 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≺>(nn1x-S8^7RsoL?{Q4gS6=mRW^m5yJ-L2#qr8|$HCNqkIk?1}z zE4W=R)lw0at?OqS4ozR3AyFen11pqE0fa1Cv^~n3F2v7T1xOzHDd9kF457xrsuxw6 zIse@oq{Da6QAQQ(o9mdfigs&Z%ERIJ(95d>F@&_#@StED?pc~P{TRA7Zd-axmZx@} z%_9k;9*1Y_n{B!fpO>N=)i-S)u6H-ztv+8l7>RNC&nqycecnB2B(@S*)SB)0{`}rR zwZmD)oY_-|j$8s(eFj;(=R3OXpqW)nw26v_P3=Z%x8XgK%d*pa=nrj3otqPIgBfGa z&NYU$PG27e4;R&!&QRrtH1I!e=cf3V^hQ9aLMV6lq#MoGQlytWz>A*YlMMh7 zAx#PCF(JRwJjFwLIwne|*+UZHMezK$KZIUgel_bZvIYi`)It`H`m82ShVYYvOhllP zHV3mtRlz5uAa}_(iz(I~L#JLPsQ_+NOv%SdwK-i$#$=d43{(ZIJU;~!coSag!JSO& z&uPrXa$t!{2F*Fby4XM=IzfT{rUXbvgyb-3_4M^YxCr&-Ew<6so*TMQt9O%esn)3E z>>;H~%c9gSoH`jzp^=k8N^E!ub|{thqzW>-2BSQt5++8947LR9#a}c zW0^(+ZDrIbm1c~_4hZL>qOz*(kdii$v?$0+iQRLj{`WP?iN=K{Sy|F42bf#VB_iIw zD%TzQPl^COsg6K7b)ZiU6q<_=k_a(r`{}c`2|IwHN-IWJbPhnNz(q(zBp)Xd5t3S) zp+HRICAqPTomdt{5TR4a(2X`{ASL1=f@Nv=LmE_xwZjUzDcUf{e8bQGK%YF5~pX&%{n1- zR>n?Yk(>r^wkczhT-)_D)}yaSA|jGjzD!>BB$~5C?|aVen%1_K4j?y5GAN}iyi@{% zz*?}>QfEXFO68Axl7LcoIFM8jU#ox#)VGn^z168mPUsTqS)^_5Uaa9JDlmMY+ZO?5 zCn6+L&VEiYQ^5f_BC`AM16R8UFOjZiw@!><4S_UvY^h`!hL+-Ak^;OGI_z`Ex~VOn z;^w11FOp7~JTI126)XZcNeM*mWs@E7O1!X8PT(xgtOZ!QbW|W~6?-1fj&#U?A+@})0HAcpLPW$%d#Cuvl82G#N^&k$?aonn|!4e$q3&AOZeVx1xnnQFOx#h;o8}#q%XDDq{`+ zGNo4kuX~g~zn22y-QxlX$vg;r50-$9jk@Bn>JxH6!Kiz&#t! zYUrXMr~{hn`c$)0h!>~cih$LShxe^cvQg=99xwzoBx($cNCoXi)rml^ZKh)1ID^9C zo*sZu$w(84x?GPksde!=n%^_{^x*2pIb=!)QpWnqqB(okS}~J&wJX}V69I|i{-;Cg z8LzqpoYksRhb=Fp==5ak{ZFS!C|h82OrUB8IzcEWFhjSvO{ld*a2qJCN4{o&RG=XMEpm@DaKqiIR709m^aw={{En%o@=@eKkB6UHgRXaw z_zn$6)zqe?ZWdiYe56?`m*`<}Bs@Q4N9~D=h(rl1L6Q=mSUor0B`G;>puq|C17D<8 zk;Rx0VQ5CAJPv>3HHcM6gl6%)pPGTGz@#ZhnkEvmG}3Z*6*A!fL}||Lz!#1(nMMhL zy^!VlqGRpdh{-T*S5|$VRDJz=DTGAfZ2UP=nfkJ*rNlDyE`-3U&S5l`AnEvZCn!-f zY%G@53S<>M8=cY1lDnA@5(Wwh0^T|B{(E{U1$Ze9V6ZR}X>VJCh_HsB%p#ITOW6h% zYA7?5pzOvZi(xB+Od_^lXmq@Bu!pK*f7-F!+~VidMu;&{kX6uW^Fz8r!GMOH)KVMv zRLzL(o!+@J5h=zo=jb{lQSeF3(-RWJ%42$xA|$7+*CNy?<(b}FC6#(^<-TogU0|eZ}j!du-SXYb%-7k2@tPCv3X$%K~qTt)JVz9 z>k%fS+p^`uB4iW@z5kU5AX2JON+kL9L|QR0_UZJ35T1Qz1)62#_IG)-D6mf6rfs!$ zLIp5qjX3)~=~%{%Tjt54j>+?UT9xk_wi!iqM1*|Ld4)L+XJyl?ejP zg#6X4`UUTnR;Q1OQX?IO1L;b7L?$m+CpCUbfbz(TgeXM?LemG2kQ>)O9a3|6Xc3C6B1FVQ#G;efxI44*p{RcP ziO|0tdiDMl_6AZb^OZ%`RcN-FINL#+37C?Qdi+KP=}LBuZ?2&~h8gDpVkt0$>@`#m z?Zp3%EZ&JQ&^D;w7+FQQR01H1Z)MMw$;0(ssHBHqh8jI3AZxT@Q(K#HYmH4pb0!7$ zfMFs==`{?7n?gY)_RZHe;{Ag`G^|&YR!1+Wp{7ssEh2smPTh{5*&GKkbz%ou$N3G3 z9H=5B_42(UGN2xCjr%M2NGi_V@A>SMQ(J3psR7MdqJ)|=>T?zFO7+r36&{g+no&&D z_sff=S<)Z=ZF=ODD|nQ`?V@SUNR1ajNCGlUhiDkhE!;(ogx0*%%j)xDwO^)40Yw;8 zV56HYVVwzy5_;T)NW#F&T7?TUfJ&JdKmpqsMf}TmaJ-0ArjI!M)xL^#pew` z2_(XrXRj4fg`|pA7czB8bvs>aZA}PFz$EZ=!s^9>Bswe6-(MIc0@2Eh;Ve+pukMY? zfJx{&+x4~7h>U6+0wO$m-oF3dQF~h>@@SEeq6z^P#S@vq*J>3Z%gs|u7h4ww4f`=2 zdILj_vNt&>u~Fxx_F!QcrRt@v%aF~#?L?8Q*fl12!k1c#0cN0As0F9vvL zbEGWbTFJuv=#u5DKyF;Q)OIdKRGR{x2>6SvU|9fK0NkY3TfNkU zj;W+mW;!OJoKrQg7@k=VO=~&TP|j+dU1~t2Ib@Qsj2r_)8#*hcfpf%SLqf>CKl%H` z;&VsWbx!(~2@bmC)b->ih%iN}(sE|ImZXg$W_&4fa`w+<-k`%m*0Mv9`)J5@>${xM z2pw7`f({wggU|XmM9IiKxeE_{ty3#W3Pa8j2}$IVD|Qo1xi16g207F9*Q+NSzVptHPPkH zjc4FOHhQoaky-)(7H$Tl*XYaE32L3Lys(0m*>n?Dw6#y0X1PuCoag)D@!KDUg9=Q{ z*|sG{CRu8ckPexUgzNjlyl~}nS-J2Hzzx7+Ut1$^5950=0^rW;FZRMW09P=sV7k=j z^_ouUO5dhiK2Apfki$fjIZ7CWj_6QW49Sh@rm`x47`gJ)aq%UMjUOV&LurbdD1!(v zC7Dj@*#jMSW*2oEV09Df;0%dJYXp*XnuszaAGLJHR|YK<)L+W2<}YhSN=azWWB zO~^$+Yd5ZWCM44kh$$~fEqqTjBKiQOE|d=W=MG5BQ_TcF(!rS4IenCYT*)sr#kviTvs? zupUEnwZkn;?=1BpG!uH>LNA_c6lYDt63O)Sk@CZY_3cgAcqq=HNC4;+foA2{qywQI)*F{{ zD^Kf9ZhQscU%fGKLvRn^r)hh0?ihg8UhD$kEW&4SK6BwqeJ+>!ydLZ8!y&(#AEwut zq*ki8B5LhKky;07ij~a5@{%4>4+*6c@tejC4HjTZ)aQQw+TqN~hNLsK@*I*2J=B5T z*_R|KTW*_a&yGxym-P5miwWs^(qhq7cMF-09OF=@m}ki|nF zRgVpZ-$QbOR>OCamxxfrfAux^Z+{3}NEWR!Sl5=7rB9e3r^*FwS;XZ+3R;oyZ>dKtOpR|6jrYu$;m4B$c1B;>_22 zTED+elv6%tAb&NVAW1iy30soSZ;$CIRfP>w1Dw1@8(|xf-XX&a;?Of$3Cgg64bMOp zX;NiV+H}*^R!cDCJmF0|PjC_36_vd;+MrAhQO3~5j=>f+bH-ONC0eG!;R zCnfvfRgb9L!1GwEm$im!P_la<_BNi+Y8GUT@E>>*;535E_@F zF$I;+o=zFJKc96BUgIr@9bKun6}UlaUT^hzefGm&Z|i$5{0@w_0G6fD@%0WRf6FbRp8v zq|vwHPDynF`zD+on93Us01tNsKv!ZK8izWzUu$D+HzLY$Ub3sK8;<$K-un2v+6hTm zvrqJF*N(N;@frFOUn^gk$3j=v_UeiWrEU@V0GMn*qT&$(sf2pDVSTfja<9Zdk0JE# zNlN8qy}!PGF3ys4K1I>7P-OLXg0*n_dL|QuEt}F{$6@ePW4pG|0*o}W8rPwexHICd zK0j&z4h59LJ4*3bt=UkHO$Vb+YfLZdCAxbOn-PlQd%iMoDj<_8+pr@cl}*VgOWy+n zfaNC4O934P;`%LoEw_59=lWcp>ur4}H~A5aH(csl6mcVPFAca$^t;y#w2c7V037V= zh$2TuO$ix%E-Wjr^-^!;X}uhNcRHriG}~+EbjU>64S<%NqOQK{F!XwYLjr;Af`*UIn-U@IyTT$S49v<%GP5)GMugmRowfT;87m9PRHC<(w!J4WQ2*FB(Ni zyiiJcL7U*E*D==Cz(Rb{v)KmuNoCdUfa+-RWI`WhH%ueSSG`ns9SJeL-rjCaKCl); znTp2Gm`=uaWJ0~HsFyWFC3c(tg|&Ps+PN#0wi%lG((4SLDc!%9yU)F8b=?hU-K^jC zJ(}hM?^>kWmj3i!zr7;nw< zzX9<5#sFOIQ~~5uQULA@K)Q3e6-920dIs=BnO;+sM=Ur2I2bB5fjJrBA$Zm3CkXut zkGra|)U~lz{?*?c0+y}zXkg~tjmL;@JgN3Gw7 z4D?+?^()@LyPxOIK$uT?{!hN|M(OzeWT583w|nVB>-sYuyRKX!VpEVP;K1U}Tf zbeoS@JI1eW$UkO(wDl>!XkXo1=R1acNX03WNEIXFVL`$btR;D{JXy~F-oD9w#K(2(5yRd(@(Eb6yD*%u7<=?l%4;+zbipaqRo|+Z^X#>!MgXtsfMIYX>eA;t= zmIdg+Gu#dPezo^`>2qxP|G(zvz4zK5v##6g{m?c3l6AZHK0o*K-;>bZ4%qzI*WNzo zrG4`9^~;?|oWZhoEWfMsa_qpLyYGAOuiQxiKFR?2?IEORG^Ot`mZhL|0i_; zKlVWO2beba0KMSx(+ki4^7!)j^7yng0Gor|M+Cj{&)tTfU&8>rWC*qf;ok4@Xr|9d->D@{c|2ab>Q*B&%QjqJia_W%@BN0*AE{l0sd(@!&>WCwu%~+W_2qE&1a0zdXJ?ezRzRe(9nAoEGfMM WcJ(}XtB2130000D*A2mfHc+5=AwY|l6fcD$!QG{}ySo)9Sdj*IcPmz$;_gmyC@w{d_RI6G z^{q+Pz3cwDGjq@Ev-dtT5lRY@c-Z9F0000_S_-BD0H7dmQ2-zeQ*-S|nwjQ_u()Qpux@FkLJ%lZA`3Py_pt~>=se zCkRx1<*9zQ>apIwcii6Z;AdmEF|_?(``7fs8~5yO?51D#$1G%XZ(;an00p>a-{wUa zd0zal@mWN&xJp+2ea@wRJ}S5}?I^1DBgFgY_0g-1v-+LpM~_cE=We_JRq{Q&WzVG&fgn8HkDyHj{9kYDpzx;#U{r94^R01Ui#ou2ZxSyU-i+??% z5PvCq9Dh7}`>9n>Vj}|OfA76IUuX7UH7I?2Jvwo7woh%vMO zyHnqMxM<*B|7;R~DUC(l3((rt6wgkr_PR5O5wU;(qwqfdc~+!Wq>i-*sNFS59Pk3> zcfUNf9JW(41C_)xy`QTXAMzQ;XTa z4guy3m_|icwwte-!!8jWXIxVrAgyt3A$WG+ICm!=y^u zT;$c}%_hueOoQKrlhOD)D`yvW*8fv+d`T6t_e_~wf&!1PgHH8}a7 zyRjjSm*0;HhXm_U4wb(nXI6=h7BG(m9`98tNNHMNi{^KWcQMln3nxEuyF8!VF?=kx z+<6i%-;<+us4n|R!tRA`s@#v-e5w!pPwO0DYQ1U+M07a>!ZKEL5Ej55Q{g3IhbYcI zZzN=74s|IU^Y6pX^V-fMPX41J!IYbM$7zOu`?wxm9s}pQ3>TMyU(mW&0V$~{aZ(Tz zsmP3^Vp@n)LYEx3VvW8-w*1RhS_x(DwwEt&2wu_vg8MN-i)19eCBEcvX;(lT5iMac zgXlJA9sLTDe=ob5)yXB={DACGK8?f3G`+M+hxsGudvyz%9o6@(0at!1Wk3KL`?#dQ$*>XglV*fJX7E^C@q;{6oaS3!l}|E!tqJ&c*u{YANPJ$L`;Ruj1h9~L$F%oLe(#st*{_BoE0QX!-=iO8 zp*&0xm_G?P&HCJX$9Mar37Cpl>(Mx%AmAHdotT^odKi48y_!Ntu%3V!0_3m4NkN|_ zMpqG0R6jrqC}^W*9)xh)R|QFr8t!%V3d_Km=3_Ma2$+mwux?7RJF!m{N_!cJ<|dMq z{~Gq#4(QI{$E&@A{u>X>L}kSJETbZ5Mfi<`tU6gmL!Y@c5&(xn`dN35b>aXC#&@{G zw~Dy57*{$>)u=B~rD!-{0i>u2y@buDXxZvN;XivF$9Jz~OPTYS(O=RM7p{og-~wY1 zjcdZv4Awi11D*v$b%p}GH*JTu@Gd!tFi?=%P0|!sM?r|NTx|hNz>AD*vP=uHAVP;~ zBAIH1c=KlIz)VI3*aB&Q5C~h6f8vs_i-A@=`18HZBO_QQ-HfcQE9|N%=JHeoI~pua z0KfDBCgX2UTU<`VTzqHvzqH$>(E((Ezejd|9-nV?x#=CjBibF{Dcr5$??|9wa4u}O zZ%!CqMiBsaTof`90=_-r9k3|lu!7;)_l@EV9;g7H`(2$eOpeK6pI)*qixul9KYKTV zcbXq_k8%q<`2tl%=8k(wbZ_EaZa!RCYNH)f7-SScj{-F-WC6YthFY@^SW3sK4Bkqj zol(SCTyR^KDJMb$4uM=11c7IYrI{mioNu?j>4|fp@ntFNI2oq zW1Qb}eV&N6FMtR&g>3CXuLMUz3WXL|3VQspksH?6!$~U;R5pogo}5_om@B29Zmr~2 z840y0p3Xb!PrjjQR#Iy>7De==%C1 zX=Hb6$zpw7@ZT>N-|gRS9GuNXDEx1vgBD+;cu=)ejcI)SQB=XifZG!Qru!)H!#I8j z%9G9(MoT0In{1>?EBfg!aE!?PvxHg*k|xlFV46#S2*e0@ijHgRj5&?v#5w9PZPMQ0 z7)6qb2HaAQ^Ixq{r;lMUF?c3tk9fZ`yop1glfx}rNSIK{qljSvjdoLRY>eo=q{6)^ zV|!o@6)2_HK7lb>V2u_lG7?l_Ak>eXx&#Q@tM87Dc2Ow-V2CluQ4U0F2tv)cbX*Hg zi8BV!R!h#*??13LrgPsL0r+G4n=DsII;=MRKI00<+JX3u9jPz1F$aVrvBcn{_iPz- zu6p6NO(4Uv2S#XjJPiYpLs{wP;r@>pu=ZExp6gxQ)G$V$1Y&ju=E&rjw{U{(0Mg5~ z96*CyNS`r2ic#IcltUv}+A(XAOJ0AQ)^Hzl)SPXZ=gcLVV8za0hh;=W1`CSesuxvc zwZV7x(uGG93JW2P{HVMX9s;JLd>|AwliXOr_j~PFtAhge9596R8VyE{M;COlwO;Tp zc}2i`X+>2Kyz9QG29H~>X3qXSpV?_1$&c)Oyqq^1V5nt$U@olZ5zb{JI~C_UO9pZk z0c!v?rv0=~D$dq?ZC(M}l~bC2*AuaK{`==E2KS9i;X+rmzgn(w&1j1{^>DFGNmDFZ8+Q3Xkc0?aYytO`4^7(#(s zg`k26<5X^1pfriM?q4@DFcbG#`cuA`W5yptRSBi!4EMmQ!ZHL^6%mN*gJntrJTcfl zW-k$7SRxZnAO`z%W!V<)g8RGe*$4!prFdmrb&zyREie5^44T)n*TbQvJfZ3v*aT5;PU`hYr`!t~0d0*!Ho3nG~X?y+e z*bPqhzc!mR45mU-6I}380b(3($pqO8i=81A{t}7#ixXW5nZvs>Qj=h=0KSEc@ z^wD%!zv%c4AzU!=ko%!Y6Kjdx22p3i6vv^U) zMNuARk{5A0)Wf2Ao+5qx!pXy-$s-{oaozfIfl)?NYOrtS%nyBT)qvbpKXtCpN|!#| z9gx!B>ojHDuilhOG*@LF2Q-Y_(h3A&-aT z75LCy`FY~}a-uWfXu)9=WdkSsL6<0gvhl>cZV8ESfpEsRl4LEKwa$`EU5LusSO6DY z9zOva+;TG`9hK{>#r{v_`b}ls*?rYnL9FjpzO(afl`T)YH9^BKm5^BDgaiX@N*56U zS+d9zPy7S0uO8aTA{WGngf`CtLnwiA7;(aWH;W?-RxbvZhDGgxr3s~BU{xSg zmJp?{q}$N(H9@{_y-Prd+D}>b0NtAu6=yvH5z4TsmRCTKnJ+zpA=lZDG+7pf8JkYq zNn z1&A_QVhCg_vgPGTbZG%lDmfBVy7n@jG@$uDQwyU0RN=p02X(DV%FP{D454S*Cv}-B z(ub}6B?;~FgzPGzLNN|=V6NO?VwHYa~@t?mXC zsRxMuDI}{)_uAWoq)@TGs_uR#g+M?S3OwayuPFotP zj7*S^aepXr^Bs0bQpv+5eJL#3Aj1|W5Gu&cR_7dc!=()&6m8aDPMj_IF^0iLJ7=TO zq(}yR?H6W-z1vVq;v{*xlp0Au6&YeGnwn2Fy)-m83L;M?x1nch-OZJKFQ>_2RE}QE zENE&e?S^Hh$yr^fR=TPG2NMV`lqQx89B0KjxVRo-SD6Md64QxtM<>L~-Oz6AToEz7 zX?Ayoy}sKI|8p_Yx%Bj$+`%RA=~K4XnFVc#Heo86Oh*jh2AW}N4Jb+ybqNnk)iDh- z*vmK)B~9ze7rXuW>eJwa(1Vb5n#BAOcUn>4ZB|LnOo8@o)uUu&6=xkBR7e`hnXQXep6;-qufaDA1~?)v=MXPO@z;Rx8g*cRM&BS9~R@Fxmnaph5*!~1nW%c!HSYH$r>4LZLuu;U5$eMCImJG8cL|5e*?5yZ6-GrO~}|&NClZb zkA+nbd2%(F=oYoaN6(Sbo#9L~E6yLP6w#014eOGkaDGeLwv;>*9ln)i7E3dpWM~|t z4w6p%79aQ?!~Zt8gD3g!Wg;_}Ai1TaAdQFNr~VoMASI_xPZ&><7`M)eSzkag8r|=M z5@5I9l}&ojUOfq}(n&a|DSBX9oL zvU1z(k>CI;atOaokAMh6+2!);E{c!?tP_Vg&R2}b~k8)29`&78ZID#QzqP$g+Ntk($NNdG= zLW839l9r@TZ2Emu{npI{mZ}=dM@+@B&=vF-&{%(O%HA683dVI3tDIwh^D-|5VjNAz zXv$qCdUy^aB&a1)6&E0fD@7VVWyZPOO?;z~rbq8WiM!Qrb3vxx^lJ>!E?b^~m~>uf z_5>BcAf<^~SPa$y4lcs;a_t0dFw=EGx?x+`)!A(xU*UZHQa{0_h`~t*A6;EtxDq>^ z6+inGDm~10m88lp3JZ5Da{?g4ZnKz~KoFIl!&oj=nl2*)TESc7ckV!W=nu(V`pLJq ze|#*b?{}?UG0zeJZ@VRy*&;O2OlE*o=}rs+5aQ9$!LMxl@Z;;#C!t}EEA)1UpT`Af zyflyxy6gni29g5lC|@_1r2Fwh2w^E?xI9>rSfMyMp;Y#mQft@bMi!H-d&EmTLzB0u zg%bFNn8qYy*W!c`J39BP0v?mMen{Fq&M&wtw-W(1Y+VJSLp8gTy!Yu|tFIpzXs{N*^!4!?N1Gvsn($4d32J zC}&{`_+DwsbxGUJ=yw~4DHs;xVc9;R!H!-K{WtQGDkkF)KPHh-xC|ma3`{c=xc<{D`Wyl8DTmL^7(fp!UC8i-b;yf?}(U~In9kP%M}=`F0v5P_XhE_#u^ zupJ@nE1@T(_1t3+&t+8B-NVt2n5<{xu1n>z)2w2VrKZK&BmGaGO;L$6h>8W2n^fugg09Klye-9 zFi8$}8p1%}OcvK>luwbVFTU+#q(wV4=d@~jS1TrMIrmwP^bj{>DD={>8^k3N9 z%wRF}4HE9qS;^adf#v-~P^P3Yr;#L<-Q9 zfMr4~oD`LC8}+!ZW94H%@nT}RcN_f6{rNVZcX8t2j>?aI`FB;*HcOfqzi_aVddoZb zIbYVHzSN{wU7>xS{ZTVK(j^zjyIy7zFYbhQ6erG9srtjVa!al1nfB zJ=sZE3SIs!*QFQ!wG4HlS(->MV#>U%9pzZJZ{A48kH6XjO*lpO4-qRkKo3Yyz=>5C z>_Aj$Mi0Qpb?2^G-Sh#Af9>PVf{05E12;0?o@M?m3UtunI}4;u9M*0KgE0N03#FEn zf^DRYvUKlRXXe?9t9!F$nHC88IJqkzUdylR%gn=T>(ly+}oV%T~U zJ}EwGsu`vbT3%V2df;b467wwA!QNXy08!>93azUaK_!loL4QBmNdSp+QHgOYWd=F1 z$Y@E&&$gQ+7NeNNu zHI+&vQ5YjkZWLljV$3!JQi)U>h9shn!@w8>P_#G%L^_g;d&@Q)uFEE%{AZDgK?aHi z?JA&jDqZC>Zux`#A{U`c=-d!)gC&swkn2S-je-&PwneCKX(O3vLm3TegI4%cRFX!6 zB&E80cQ$<4|IL24(C*ZuQvf5nvqVE`ujIif<1QHJD!@UqQvuaYJSEmglj)UErK(`b zNTvYnn52wndFIVW3?}z|oubu;Q}Wwk{YlX7#CI7eI=%FH)tEA7tj-*kJ-*2$Q?Ib!%G;!)T4WN&xF5uO;`^i2F zLLNO-Wz(mQW!6Y({F4b}Pg>W0wzT=JX1{X(wlQk~>?tB7>R4`ihwV=Fi|9hl_sPWf~CKOe1DyEuO$s8$*f*OKoV9q zDX}Sw=!wMEE{R^=_2*i(oz_VN=Ji(iw<1}V02(bTi7X|h`(O;;lB4%tdAAK021LL} z%DOh#IW3R!1u8B4Idw5lzVIu_MUvFii!eHb6F?dv*$q3F556-FEV7YSHCQQq&Ux{L zd&jAPA1r2HD)-SYHU{V%U6j+!?8`5QGR;BLkMqEL&Iu4jFze0M0uW z-eIHRj>o&7CeY!nu*~u9ndPK(v76u&*5egnTUnqInlMa)&2(wM=%c}>!rAV}Fe-2N zTi7+F7pN@4se)29v7pK@2NOk`Yg3wsg4q^78Eb+Sj;K~+vUy&$oi;U-!*%PbDl zs0268npeo;LY2roEwJ#+uQ%B^$lIUjjd2z&SjKhh5I4wa{S#Ap+%YTYOxI1wQB8mk zJtun7g06PSz91qCB)r1E-HL`QO6py~KJMi1{Ax;l!>J~YlJn1|wdPG(c6 z__?$Y3S;cXU)?6EUA3=SvK{nMl%L7rdY5zNW4D%zAPn;d_(;mn#DO?)KTAb6q{k8l z3loX;DghwsNn_^0V9^7R?M6mEDc$@L9t=%y;b&swrvFO*>-(}2vJZ|7#U2efwxk$` zO~MOqeee_S`SuCMYGz+4ky^Cr#_bScD*!nc9J)=xbke*=0rny?yI#Rw$x?p3)- zC=&UETX5S45grvT)|UjKAbrL)A7+CF7mg65uV^!1wiMgaIP*~F8%0DHq>?mpbS20M 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�arl4fXP?DS{qU~GLo9?K@NRRYkR$^0B}qyfRxS51Zy23{r+{8_OFlh`6zdn$TAg3ImWs$W;8xW z+8?qmJwr-?G?jkHC>gJ^vSDpg4{irKho$!_N)s?(V;vGD!USysCP_2!!d8Z1PUnKv zzqD}}=1LgjP}Ua9!q}p+^`bE4bgoyGc5^sPt-}<+48R<~0>H{6MjDhQF_Yxs+kt7} zEXrCUk7ja8i4da10}vUMv919;vP1++7&~u{DmV%l4pa*xVC0p5x0xj%Le3WFX2v$r zfs#~-;Bd@FR(p`E02pyz$hqQ$tt@<}5G{DzfW1^TtAn>83&arr*1z}@m+|%2HUpSu{5Wdrq#Qvh%Ijm1$~f|2fl3C* z*n4-UHN_s@7zZfP=-ovVpt4l9Mphlqb(r$BC`|C+tRBVrdTknp+^RHC3lT=g@tWtk z3Z9%XpHvAHK6=Rg`zG98Yp7qJBegCc-&lvT@;~$Yt&2%@wJc~IWvJ|PIXo^o$9MpL zW|tMq@5{!M_a*Nio+hLe0fELiF<)wD42kh@1pd^!0oTAamI3#UgXF+m$PMG1y|J*3 zx@onbFCOLt`fz5Lt5AYDo@vaNRJKx5V7BlZ+Lz~&MEX8!G!N7pk@$fuPazH|pA+g% z{Z|{0DHw;#7c$J2q*d4)B+A3!+c}x=#7!Pbx={Ds-O2GE{qoDL5Sw#P1{zr}_+ zjI**LtOFY#1_2F=yC?yG@P)1hUK&+Q7+j1EILl_0zNE8fLv7bRP*UbU# z;zj&BtX54WLRt?7^tcjL)eRoSX{eH-tJ)hpc}x5wr5Tv~{n()Lmh2i zRhR%cat_MnMBd2p;o;iuDvF%q@?2FDa_oFLh)a!K0?J}#LdmkKE$$waxUiMs@=k{7 zLSx~}m+@h>J{{W}(F_KD2Hnq@E_A4Ha9ORWY%pRhp{hmrZcltX zcsIg`Imtyyq@N^_kkYh_1kY9iK7en9enjNMx?DxUqGNz2P8}$U0t7 zNrCZDwJdm-VaVx1OkfHgTU#Jg0SvO1fFAac&s@mEWLB5;O#nJUX+fNB(3&)S z1Fms%t(-OkORKvUg1#zDC`V=kg<%IZ=h`qEft8<1!^Z0Hqzay0Nr4JPRn1a~Y$U^a zb12E1KGv^SQT@BmYjOK_Yu|W7#Hkg7s?$m%7Pny8tgdDRgOF@U`RB30j*)pb#=%+i zl$JSN?7QNBpDpR0(nPEQ&x4%S`u2XgR?g#yl)XMjtuX^KPAlT5qiZl~-8js|!Evum z?X?BU%B+nCe`+ZJ_b4-AL@~N*qcO-r^yVksNM$D6MlN9h) zxHfJqCZh`8W4h4L+Ty}i)}Y9lM3PV)pDP7NvpTFX z=jg$>aX&`q(XsL6L(E2kKZ}h|%)L-v7M`O6q=1UN|8k{6`WBbW;%CMvKVv{Kv4)#$ z`0rfLj}KM%&~J_!{Dz-fJeM*Kr8%@8l&%)ezFLDXf*0f*3Oo7ir5r{(+o(-Us50U%}CgTKo)_`eD9O5s;WTG??IGNXH z^6iaj_Yhb|2VO}L7JF5gRw;zNvep)r2fS3KTKTRNWDJG_g&TKPEq*p*5ZtMFa8fsX zdS7clnbl!Kwi-Wr$WIJ9N{Qh8M5Gz7HHG+`=PM-wXclH9dh$)Ir^xv+mnTytFvuDr z((YE;;?=Y6e3ygH;e4xvsL?|AyHOb%FeX8@IQil4Hbf6xI^|h*^QDG1bWk|*iajlt z=#15^@SRgLF42ag@P?96rQsz-rUbY4cR9h3kMs?Wx2?NxFC} zh{*@K)-+|hhFe?fxbo9I+&ns-Yxgn!)0OJt$S}`T@Z>@?eG4pK7whfuNUz>=P+Mp% zeZPXk(X??8EdId}tC9j&o*iOmGsV%Y#=*&guoO>}lcIkQC@CPxn24gXF3((WKuIEj za{W8nbEE_h)kQ!We9bgc&gV@u#E?6|xT&Z6`*Fy^HD%*Vi#+p;fv7kg;KN#LhkHjQ z7Aw>8+MJKYix~fz*;5?;u|;L2mE1=NN>V9kI@i54<+65|E$SAfnP;~1_l-l@M728{ znb5*q^HBaOh`q<&Iu3PbOM_4E6)n%ila-HWnzZC4<*tV(RbYu}O9%Y!VTn~?S`=AU zonfI$z6Ty#&4_zhH>F*zZEGSlK;D>f6>j^EqXexjj;6IQx6*sF*YB2h`DgJucKhI& zvAFO?ial+}^}SIoE3+1vF-tTCbeAiQ*^(kv?mq3qkGuFsQBnHKj7sNR>pp8u2IS5` zf!(bXKm65g+}c~KOH+tdc&mgY&9 zv6ezO7tI3D?b+wuNx}@)aR{F#Dp+S;5BBWVwE-Ef7{v8$_Ldxaa`}+MIW}yJK@2_9 zFaDfZ>YO(R8>`rBF*adrBMix#o$1bG1YGDi;}L3HGp}lkqO>^jxpXzFTgdoyu92ry zx+-fIc>Ij?^QdXmjhgIysaqqgO0QzWJq3J_S;Ns7hYMR7p1F|2IfuQY3VVkYijrD} znOYkIYTa(gq9kJz8&*c&d3sbAxNGRTU9heT@->XUxKh>d@dhJb~u z-P)dwqtLirms5?|qJ|J6^p+?o@X@C$T)n=)a;3@PIY7x~ukw3QFOdt;O)eCa5{^B8 zpuziQI_J}BcA`(j0Eo^UvNEznV`% zl+uRX$cM5;ijh1urtzv}-Ly-`^XH^!!e^~5jw6GkeR+%5kJE;jQ_hAUmUK@Q89@GC ztI`DOu!-24aabRJ&-gi$2Ntb=>-Nj9=kH`3m2O--;mR4A#UHHs25uuEz9EW{v z)K>~GUG4#xxLn8ODvluHLm%S|0bGf~w762y?Zk?KoEY(C!_cRX*v)t2Jh*8#@q`)O z@bFs?(u0+8*xDy*!2+$rfx^xt#m0y_{!V6fAj&Wp#-Jr@*vH5KaxcMk7vLy5VNPOe7p@0CH5}oC~yR=LD(_=>5d20HQ~KJTwC(p=2cLsK+~A zKt2?MbiIF`t#5qT z>I&@Q1l{|8Vg|rj2az<>d#4KxZJOSE=bZ2%ztGyc{(PhjUT{9uZHHv*I2tAaJc>6{ zoOWdFaYf^;S-3+<5g1|d?eM~raS$N{W-r5rCjJhLBg7AfUn2!o7?OlMRb0a1Ff(*S zMv_pWzAM)?61#$uOibz9@LYN@nw=#=WBwNg{DFt6LkD^XDK> zZ4m@m;t)S3UJY~NRY*b60jEEqjN0R=<0ydFJB~o&wU?3%4D%E+hc$kEoYut$Bj$^6 z#1?mJ#?Lbz!Og6{m=0|ns{`qP@mNPJ_?Wa4J4lW)V)`%d;jX}EsCL;Vnp9GVG!c%Y zXvV9UtoBsq0&tt9ib4(^6exA1f=<(k0;BcUn=dy)jAxq91#exE{>!-B%9f-x=hzNmd1d7CZ|)1gylv@ix#(n8x7cdkHt()stq=jWokkqhGX_3Ry?AeR_fA}~;{DJ)_}x7fynWJO zN^DSg&vMx>c47>Y1rVp28#|eBmPW281W|W%W1oYpzn}sZJ{D6Ge-v6 z=-y@#u6pE|LeCj%bz8G8n|T zA*U71c_}zHwDEysps#Bvc<;rIh>`iY>cj`U8=Cj!-khrN@9}#CAy(O>l3eQU8d(OI zM}s6b49+Z~JSLVacMyQcUYv<&(c1V)8b!Wm%H?p9OtcXkDJ@?Ac zvpbp9xVcy0@=iwm@P5|g=7>tC^QCT8U)GtPZ8lsi|Hh=7?m-cPN}jx5nB>IIliSFr zi!utqxs2!5@_dd~1LfPDuH3`_-q()HIFND8Ko2fM@_R}_qNp*B=3Ik!_`N5h`*i$y zSwfLOS-G{;Avo(NSq2nBG(2@r-T`DIS!@OJRJc4-LK_q6unkqQ%?I$^PGJGq6300d zfR=Uc9>`GMF=QY_+DK!^yz=RS2p{~Q`Cj5K!ftfZH9-7XOmA*eP1}3{K1AFh*JYN` z;YHI1UAtDNSX|QVYA3|FNs=_(a{TkveZ)tsqtuN7)L3>aj>0&HjzQVDgAo93@0VDW z21PXr-?jEF!FejM?0{@+VB^EijwFu0x}aP9o%#1?i#kLnUEWCdclw?why8t#Xp4{U z`(6eaMLR(m$u$lkjvBfKgQL^Xf1vfCP9BfVjRHxYiYh{+WZ=l<+-k0wsDk4QhMucn4k5ubZZq!c%F#aCs0mv{It?z;&Z5UWYg`Y4jtSJrF;Wn6$&Wam?=Q zGaB(d#P?K3%DVBsioI(*wJJ?SNbPv9#Zd!0wb4MfLi_mrc@1KI%(Ac;4Tv($|L#9m zDA5?pN9h23WCOs8K>SehOoir@urNI&o@LQ2!e}6y)=2NQoojFd=i0T>m^i`r(0%`I zDY0wei^_(3$kO#v4O{mZjAU!>oHszuwTM97=;Sos`299yUw+^He%+9@4;1TquDFUo zWw&Nf#_6dIS{wZ|)Hps&>hZyT0P+ArAFpR$Lsl z^p!c|@7&kQ$)!l{sp!&9K8M7Hd8a?5O5T`lZP@!6=upsIGI)3D@DGJZvfO zk>>(58`z=NP9UL0-rY*EDs$XDphlD;lJ07dSr;(@v9?%3$Z**Eyy0jdPazaF_rzYN ziw^gt5cf}Yn($6#tcr}*0qWL%th}Z>-+Ja8L;IR z=ZI47;=Pz85~BW$^Z>Z_9}{HKBs6pgB?X?lGzfL)*%G~ZCD-MdQXJ=>F;%jx9i(sp zH#m!)$q$&VG4thPq6dfVXNBu=ZI($!EkRAs_>13%4WnbL>^CDR1>3b$<}6&LdkDjZ zj5c)qKHZktZuv6CX1a}7eTTc%oD${DA?>m2_zblLQ>WWIm(F^TXK*|Y>v}b zD%Ne_>8?lDI8b!xV614RWr=De;&@hK++zZ?p9%k7t=FRt-dh}DZH$u8Rmx1oT8oIa z*w*u-w5Pc@Rx^tGI2v{!-CLfK`mZlzJ1GJ+pq2k83SlbO6RiUDZ7h=QCiK=H4hLAA z%xmmSQn9<0;@REIKg6=NsJsZI1waco8Z;qqpG6xF)mkpvysll(We#?Xk}@FE_9CM7 zpetuv@7;KobsU6A8}Xqb57dvFkp5vC@wI-agoJBFI9&>KU+#Z@q?|UnilYqI1{b?3 zL*4i8mdN4=C)RlEPO@ch&KIX(X+kPGv4G~`Nfic_NI?-}S(!D8HSHJI%O{DSLp*yj ze!gi!pAn~TB->@9FxmxgZLABJ5g_?zuZZP)UK|Sm9(|W3g%g z*uH3s!NV-2u14({w~PhkzGQfhG>{l%$(rzm5Uy1&jH?J;FR2Z^k8$guTkL_;q)oo4_37bMU&z}< zi1LTo*G+*tf??9sbOZ;V989;^@j?t+c+ zd&eM7U(Ou&J)BmP=7<2DDKBwJGpv79;_TL*?^nU;vfrwx-`z!&&)7)CI840J0(I4= zIo&DSz96=?H_ei^38J?k!h*qTusR(@!+rcv8_G!DuOpMu=P6F07OhRi# zs}bp`Rsmu@!x3U`%Q*fYj?EgvdQOW5-axBO&SwAvNxR9R`u4mWg9q{M#rj(+|ff*=dGErPXX{Hg& zfc{D;p`;5TIgcirjHT1YinzR`4XrKbhAh;|-DI(#DBlZhdfH4~^&Ju61zaKE=fwRF ze80?3U6n@Ado{EBm>SOa6Y-E88)N3iOF?2V^h}&9Dl1qQpFzQ{j2r{29D$98io5Fs zgJ%5mB$6bOlBOEh+M%F^374f(a7fQ>)UiW}4K$Zg10zzlQ;ir%Wl>pc0V&#mChx;G ziLEWm9?QI22T^P12AK?C?y}G!x{UYK8YuRF5x}|X2sHo9Z<@5x$giOZvPH>J0YBNE|$rPJiI9Db{rRT z*3*Q#!IW$DM$my$EiWMWzOAEwhjlX6xz(Z$8!k~&C`Er(G!n)cG7Q4A@qG8B2;b3I$C`@#p29ij zq?GIhg$hxybAAnl_t=githOQziDMXHtYo9-JcBIUt2S0x8y!kZ>ll}NopXYtCTZa; zMS8*us0t|^qyQg&;U(jplETG6RZu?CRN56n2qlG!Fcine%qaoo|89(PN(sS6jSrHw zjYHx?#2g5vgmZprLntL&Sp(J?ktVb@N(xk7pHfO7SF~SKrQM?%0N0Z* zxX!(r5PnUHXHXIZw;f#TZ$y1>j%k_eP&PLu@ zrr>b9&7LE0$#F;;fH1NQE(W<`HlDAcySU0>EgeQ{iM__yfF&JE5r@t^?n^0}(db;m zl4L`|#?V;TGFE}bSvG3FapR0Ow9NccZ3Ge}{kpS3a)F=F^c}RzqTcbjEVUv_qys1{ zk8*Zl9UND?!cY?lNE_Jm0r&8cgLBRaZO9h;YK!Bjs_FD3Er&raosvROCnR8egdioH zQuJ)Caqg_V|2!B#-y6BO$%RK8$KG&N8qtSnMolq6*ns<;+48e8j})}IBgC5*S8x|t zn93^A8VsSNaJ5eeto7wS9|_8cYDeM|Ccv@XPB1n=bdQ0=Bkcd*-nH#GjwII;nWUtu z?wR%aWncFH|7sr=elTErUDH*i3(3rgeaMK+V2Y|sPtC%BML_6Ax5P^_<9_0Vn>K9N za1&ySl|y-l<$0Fv<|S?HAxrd_R}-18lu)sF?fk`!U-5^TcOgzV>Ke4E&6YePEJ-M3 zmdnyMeF%zgV3ruJ>!H4d86|2XKky}Ik-2yl_1&zRn>3#7SaQS+h{2c5)2oxBV{ws( zArNU8X)$}Lm!sk}#(}E98^MVs;B1~j%1j#T3v=0Pg@m(BysD^A?cx*>oTp47G-G4G z$t>i{q8buQa89F#BR}k;p#*6K6?Zw4!uX?@m0UuHwbEP9gfIma2$JQ>fcwAGtq|Z= z7{D6NG5{{6PLZz|cksKeIh5`bB!m!46dwzGL z?=D^$t+3<0>t5VORO{=q+x4GPZVRPIM5@U~bwi6~6U0EGmcHgdM5MTY4qM(?Ey_|$ zd8Py@C0;cd^|f`NoY&m%M#V1DP>L0MPTkBuA|kPv{W7;_tbF`+&Bfx@W+Bxgg#hsc z7na4=7`{AT6hdwxnf162%6z2HpmDf{3A0pTvqWUhLV<#Oe~jeo^F!P=aQ4i#k&9RB zPp;ZjLke^4GnX)D9Lj$4!f$(La%DtQ9%lJ1PodYOMFr8;$tUYXge-}dvq+h(+4iRy zLmL(Da~|b>vM@t(7Rf4)oza%JjJQ_-^p5oxEmYP|X5l4?5J|0>dZb!m3Th_L@&?ht z7Go?!D!r@c`SWc)o+MtOoxGcCJn}JGQjk}Pq1r#Kn%s}Q3ITAr3SYLWE^57IX3UzA z-0vdA4v}asA`wZoCJy6m0@gR*Xck+H>N&Ya63jO+v4jYUj9aaR2OXIKsmZrM7-_v;_I+lq@5T0?R;s8r4p9MeslpQ?BF}{F?=`cC z-|JB2VT{#bEQBovfHIHODBokCICJa#ki# zmg)wYyXU&TBqgTe8U-IKlXIt6T2X~xybDf&5X+xr@$MQVRK7kARB{bOz|g+4*4(R? zLbgVhSaut6Q!t2VwO(-XrN3S&Sw%%hviRvkiZ_rl&Z|*OS@pt~w=9VnYA-I4g@Dd| zl|aCf>-&{0kV?buug)zVZ_pH9Ns?tv@*OW(m_g?$OQ*1_r|0nuwO~PT$<8%*g?UTn zwJAE3nA{VJ9%Rq!6G~F3j%7*34YqEuVF=`9swtxOVp)Je z*>+s7ye6os2SsJ73>9Bh#PTc>2$VOJO3g_a5-{lfQBlG%5)SI7_c;~&y_I&=7!+YB z|3^--*D3@Hh`A)eE;CCTM%$>jR-P=ao760+X4g)s#Q$m!O6_DopDe82fWsh=h~To+ z5}}C3{$9D;eox+Wbp*CH?OBT@&^(G5Xi1eP#aIj}Ga)Zb>gLxj*-K_ji{zBJ%B6@b zB1ledc|#GjJXnuPbzc;Bl0^hF12cdrXPM4Zp7$fA!!GcCB#Dv2qMaIMnT1GYVOV;JqQ^tp^(KgO$)q{TEg6b+ds&qpCI`v|*gO$?7gNjJ$11 zVZqWthRurWhLkN-rJmk5+=xf`&5Htmgz)Vl=-Tp%o;wDD6k!%W-;TnnYq5Du8V-3l zcXDo#0gYTaki0AI-DeNj5i45?YG_%Wp(zkw*|6}^I$femP~H7tW^(FDdf`U_z16S&g+<0$Vx0n;rl=t3T_B zCjb`~Tv%{Ep7Z6f3zt9tO4EK9_vOZf%$+lEj0r``5Y6P)>848(@v*We)ReJrECGmO zD@_I9;Gn8QgdBE*W`0X{+_Jjn5V3M+>kPE#%Phr0 zjX}nN$cBox5!Z|*wS?sufVdCq5$PjYylHh{-`Yq-0WxS}j~y#V`8H zjV;B~W7{*OXjiyCYiqT5XRuB^P}TXtQ;R=uhH7%hV@zDnUk z2HO+g9^wl1Xs0s;U_ztTk<`nOKp@||RQ6Jf;ZtJKSi9;@oxew#0IpiueqZS3NMejt zS=w{?gs9K)SZUJ~l_$?tk5qJ`ll=O9poe|LkJm-!r6N9czDB^ef_0#AAnb;K-I%A8 zczQjjX6Xm?GF5(lPWeXF~xjda^Tsldzmva7vla$8$a(&w2m-G4H-T#E0*X z1LZ6<%_T--DfM;RprzK?n0-u0Dw}(P=Ionn%Y~%Pw#%ysMj}XOXA# za{2MPoSu*AG%tJ-mQ&6}HdR(9mEFAI*<&jSC^zDN^8n5wII-Y0XE`o}(`DwvKYlDj zj1*(orK)6C6^+w_z~>o!u;jOM0Nq25Cb(0qZ^xW1v&u!^az<<_Xme3Hy+ z=v$CUbG6BkVJrw-@bbNY8(p-KYxwgU(WX~W`_cO4rQ){(HR6<($*#D8E?GuZfP7BH z;(caMK^9gn=V2GBwONu>+;~5T(;J3ThO6Z*N8!~3!1Q8$ckC5l%U)MluF7MK9mfFH zIT0IwZR@ZC809u$$$IuJR55G21>5!SO|$B%6wM@sS-+Frf;&EmR1rShJc}jNb-D4m;ZX2Uw*tSuP>+kIxTX{EU%g6 zRn`i?djPyu5**%00A|%7<^fy)97W_+M1Cv@hd_Ba4-}uC7mSheybO<-WpBH2HwN;6 zl&h{n-TZM^aMQc7Ev&V7#u#we(TbeYx9oZpn>CpDdbY9i81y%WM&Df;E?U={b>W#c zYt0{Y^}_v3c>mIkImXILoXitgFTpR5v3CAlCa&i&@(5o(EE8t1Y8Gi-cNWE9(Lo@#jM&jzdm?6fM8|)RLc~3E1PX_Xe z)^}Jgsf;}LYa86XO-@-^%kdCH%@wqATv$D?!LMuMmvhOjDlkMqWPLx=Qr`6#34i*x z5%a?MpWo)Hh?k`AhgY5*jNZ*RtyQF8Y#f9C{WRg}dGVZtk~KFnO&QY!i7`*db3Rt# zpYuyf{LF$M0A2wcT^O)9FCcFv0^h0vsSboGU)1viK>#C(wYOn++7jCPf%IS@^k(8zKzv{ z$AsTDwRr=#I2Kmt;I>Jv*~faejlk6BIJ|(@UU&HU0?ksH6(|r^xyr~}J=hM*N7zK& zryZlp6t8-756@AkiADIuOR z841?OVV;ukyl}cqJe{X}I-c`!n)CCt$ZsroN{N4Du1a7l4A=zvZ8Zb+9)Kx;oqqP2 zu}9*7A>edjUXm>5DNnEGbpH2Wcl*OWjC$_48v{kHowI;^=K89`Fp!%w*n~C4mQ3_n z+1#UPS?fsuKUqvaD7XtP2Tn2T_FZpFgZ)O8$+EhXT&N# zi`;kTnG0L(ux)ByD}d3pJp1WE$Fq`eWV!mCe-AY*YZZm{I4oYTa}U}vpq2MhY<#%C zp1)3iAN~JreZPNx`%XNzq@Yg});v-N)$OUZ{QdZGZ2sK?h57if3-#Ughj|hJD)hKw zNkYRZY(0}%eyfV4#KMA{5~t%iFE6J&rOfAPkyo|+Pbu;LX2uhM9{^r9ynvq$0jvse z0boKs0EoH34j4n2Ws%F0=rk?zc)pwu0QUO55F-U^Uf2o+`jS*%I(d(q;MRFoFx4!Z zSbYC|k2}A0cmcH-+lW5;VH$QWD;K$Dk}G^ff8AR%>-*>S_iWcc>_Vm1K2Nz$2Qm=W zHT&?k(%E`XTi@x2rrtWwHuLdS3-Zmm23yza*RQ{Z=UnJ#D}Lm8`|o+$xjGiT8_3#E zkA7?Yw$`D;#uT)@fJG$bEK_1!gyoc3URm%%E&mgMe{>4qyio;^&x8Q%6hK&Inap@H zCL}%qc#VO+XQsy)H~`oMD$0UO{|ymQ#Z|lhSA4~>r3iec*w^2`>NpMog92ZAE4Q6! zq??N9mLhxanYX@szEHJTE84Z0KeT>tDd;Ul^_A~$>wH@SVLtQw_paMB9ltdhhyq>j zrJ28STU~YK5)q3?R+OLR;!Y{?D}a~Gcvivx4ZstCXXgQ!0=NYPdJ_tqRZvX{^t&Vh zm@?x8;FU!lk>vqR0gN$FRDec^fhZO3f5IPdYz^vY7sgY}S1;bWPH)M6zp(I| zo4OxR@{xP{Bi89-@9kr2_fg)%&tCt%=iXYMpV?Pi=h;x?t595V02#o-f;mvZ;Q%;^ z;8kP(7yUfnu>O1l2XK=G$XF_(bNfei?H>Sq1MsLH`+F6B0DCGPKtLhdA)wYs-&Fv9 zJw7_`U~MHn_uHRR)$STSWA9gA=hkz`RoUz7pKo0UfB!oD{l0aNzh~XHuJg0czok?9 z0h=E^cmJDP`{a%Lmkm#xMX)%O-^Fk_D)1-wcc1)A8zI1l6o6IfNdGiNV2QwkeoW~1 z?$5m{i|EebKOa&8_v3#2KRm9;cWxx*Czs?iWpHtBe-8!zDN@izOtzgPFqGiMDTY!1 zj7|ZBPU!E#e?RWW$CLnY-IO=0+|BMYxqn+e;`)C^0rZIpb_v2og}#>k+@GQI0Pezn zKkmoRc>-PuB&=cj-qqiN{^Bhq@KJ&Ef*f2}(0{_+^}iqY;|~u7yhq?aJ?8&iUciqE zq!(c7@&k0k;)ff*zaRJGeta$rKquJVBk;^Wn+-o-p#W|vf?gqP{Xg!)e?RWWPb&dn zo2gsgu=esWj5xmLkhYwPKf3*Vmh->fvU+(Yne%y~w$<})o z{nHlzs};c3x#Vv3@5lZ4!@L3dy+Z%5)L{4He%z1$!?N^GOK=-W4@um`00000NkvXX Hu0mjflej#Q literal 0 HcmV?d00001 diff --git a/data/gui/online/menu_quick_play.png b/data/gui/online/menu_quick_play.png new file mode 100644 index 0000000000000000000000000000000000000000..f31847c25c3abc4084d3f367ea4e5605cd5fb26f GIT binary patch literal 60414 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�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 T>@R9!00000NkvXXu0mjfO0Ofe literal 0 HcmV?d00001 diff --git a/data/gui/online/menu_quick_play_hover.png b/data/gui/online/menu_quick_play_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..34fa66e48d77574cc8b3df5df4c2e9183b748241 GIT binary patch literal 65876 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 + +

    + + diff --git a/data/gui/online/profile_achievements.stkgui b/data/gui/online/profile_achievements.stkgui new file mode 100644 index 000000000..d072200b5 --- /dev/null +++ b/data/gui/online/profile_achievements.stkgui @@ -0,0 +1,23 @@ + + + + +
    + +
    + + + + + + + + + + + + + +
    + +
    diff --git a/data/gui/online/profile_friends.stkgui b/data/gui/online/profile_friends.stkgui new file mode 100644 index 000000000..b0c6b073c --- /dev/null +++ b/data/gui/online/profile_friends.stkgui @@ -0,0 +1,37 @@ + + + + +
    + +
    + + + + + + + + + + + + + + +
    + + + +
    + +
    + +
    diff --git a/data/gui/online/profile_overview.stkgui b/data/gui/online/profile_overview.stkgui new file mode 100644 index 000000000..ab7a4d2da --- /dev/null +++ b/data/gui/online/profile_overview.stkgui @@ -0,0 +1,25 @@ + + + + +
    + +
    + + + + + + + + + + + +
    + +
    +
    +
    + +
    diff --git a/data/gui/online/profile_settings.stkgui b/data/gui/online/profile_settings.stkgui new file mode 100644 index 000000000..35abe5ee3 --- /dev/null +++ b/data/gui/online/profile_settings.stkgui @@ -0,0 +1,29 @@ + + + + +
    + +
    + + + + + + + + + + + +
    +
    +
    +
    +
    +
    + +
    diff --git a/data/gui/online/recovery_info.stkgui b/data/gui/online/recovery_info.stkgui new file mode 100644 index 000000000..036dba116 --- /dev/null +++ b/data/gui/online/recovery_info.stkgui @@ -0,0 +1,23 @@ + + +
    + +
    + + + +
    + +
    diff --git a/data/gui/online/recovery_input.stkgui b/data/gui/online/recovery_input.stkgui new file mode 100644 index 000000000..26a8a7da0 --- /dev/null +++ b/data/gui/online/recovery_input.stkgui @@ -0,0 +1,46 @@ + + +
    + +
    + + + +
    + +
    diff --git a/data/gui/online/registration_info.stkgui b/data/gui/online/registration_info.stkgui new file mode 100644 index 000000000..038df1d30 --- /dev/null +++ b/data/gui/online/registration_info.stkgui @@ -0,0 +1,23 @@ + + +
    + +
    + + + +
    + +
    diff --git a/data/gui/online/registration_input.stkgui b/data/gui/online/registration_input.stkgui new file mode 100644 index 000000000..933639ae9 --- /dev/null +++ b/data/gui/online/registration_input.stkgui @@ -0,0 +1,61 @@ + + +
    + +
    + + + +
    +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    +
    + + + +
    + +
    diff --git a/data/gui/online/registration_terms.stkgui b/data/gui/online/registration_terms.stkgui new file mode 100644 index 000000000..c2c4d4396 --- /dev/null +++ b/data/gui/online/registration_terms.stkgui @@ -0,0 +1,39 @@ + + +
    + +
    + + + + + + + +
    +
    + + + +
    + +
    diff --git a/data/gui/online/server_info_dialog.stkgui b/data/gui/online/server_info_dialog.stkgui new file mode 100644 index 000000000..9ce39b949 --- /dev/null +++ b/data/gui/online/server_info_dialog.stkgui @@ -0,0 +1,33 @@ + + +
    + +
    + + + +
    +
    +
    +
    + + + +
    + +
    diff --git a/data/gui/online/server_selection.stkgui b/data/gui/online/server_selection.stkgui new file mode 100644 index 000000000..c863b9cc2 --- /dev/null +++ b/data/gui/online/server_selection.stkgui @@ -0,0 +1,17 @@ + + +
    + +
    + +
    + +
    + + + + + +
    + +
    diff --git a/data/gui/online/user_info_dialog.stkgui b/data/gui/online/user_info_dialog.stkgui new file mode 100644 index 000000000..ae9e16d46 --- /dev/null +++ b/data/gui/online/user_info_dialog.stkgui @@ -0,0 +1,41 @@ + + +
    + +
    + + + +
    +
    +
    +
    + + + +
    + +
    diff --git a/data/gui/online/user_search.stkgui b/data/gui/online/user_search.stkgui new file mode 100644 index 000000000..df933620b --- /dev/null +++ b/data/gui/online/user_search.stkgui @@ -0,0 +1,24 @@ + + + +
    +
    + + + +
    + + +
    + + + + + + + +
    + + +
    diff --git a/data/gui/online/vote_dialog.stkgui b/data/gui/online/vote_dialog.stkgui new file mode 100644 index 000000000..d61c206d2 --- /dev/null +++ b/data/gui/online/vote_dialog.stkgui @@ -0,0 +1,27 @@ + + +
    + +
    + + + +
    + +
    diff --git a/data/gui/options_input.png b/data/gui/options_input.png index 0163831773e3d120534a131c9799f9c5d20f7bf5..50c7ff3348df023c57a212ceaf012302da854078 100644 GIT binary patch literal 20751 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 eK!5-N*5W@l9mH{nn4};80000
    -
    - - -
    - diff --git a/data/gui/options_video.stkgui b/data/gui/options_video.stkgui index c90eb4851..9f679add6 100644 --- a/data/gui/options_video.stkgui +++ b/data/gui/options_video.stkgui @@ -40,16 +40,6 @@

    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 K%hpL-g#8a+ZA{Am literal 68275 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 literal 70197 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 diff --git a/data/fonts/wqyMicroHei4.png b/data/fonts/wqyMicroHei4.png index 0de41191427235d9e96dc940b529f6f69f406991..5c22bddb11332d6be3f8b1aef65ff3e6991f5d35 100644 GIT binary patch literal 75923 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