Merged trunk with this branch. ATM the track screenshot does not work -

I am going to fix this next.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/reverse_mode@10862 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-02-16 04:57:49 +00:00
commit 579715a8d8
73 changed files with 1307 additions and 780 deletions

View File

@ -2,6 +2,7 @@
# Always edit 'CMakeLists.in.txt' and not 'CMakeLists.txt', the latter is automatically generated # Always edit 'CMakeLists.in.txt' and not 'CMakeLists.txt', the latter is automatically generated
cmake_minimum_required(VERSION 2.8.1) cmake_minimum_required(VERSION 2.8.1)
include (CheckCXXSourceCompiles)
project(SuperTuxKart) project(SuperTuxKart)
@ -118,8 +119,24 @@ else()
if(HAVE_IRRLICHT) if(HAVE_IRRLICHT)
message("-- Irrlicht found (in ${HAVE_IRRLICHT}/irrlicht.h)") message("-- Irrlicht found (in ${HAVE_IRRLICHT}/irrlicht.h)")
#include_directories(${IRRLICHT_DIR} ${IRRLICHT_DIR}/include /usr/include/irrlicht/ /usr/local/include/irrlicht/)
include_directories(${HAVE_IRRLICHT}) include_directories(${HAVE_IRRLICHT})
find_library(IRRLICHT_LIB /usr/lib /usr/local/lib ${IRRLICHT_DIR}/lib/Linux)
set(CMAKE_REQUIRED_INCLUDES ${HAVE_IRRLICHT})
set(CMAKE_REQUIRED_LIBRARIES ${IRRLICHT_LIB})
CHECK_CXX_SOURCE_COMPILES("#include <IrrCompileConfig.h>
int main(int argc, char** argv) {
#if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8)
// ok
#else
#error Your irrlicht is too old
#endif
}" IRRLICHT_RECENT_ENOUGH)
if(IRRLICHT_RECENT_ENOUGH)
# OK
else()
message(FATAL_ERROR "\n -- This irrlicht is too old, please use irrlicht 1.8 (SVN)")
endif()
else() else()
message(FATAL_ERROR "\n -- Irrlicht not found (can't locate irrlicht.h)\n Use -DIRRLICHT_DIR=/path/to/irrlicht") message(FATAL_ERROR "\n -- Irrlicht not found (can't locate irrlicht.h)\n Use -DIRRLICHT_DIR=/path/to/irrlicht")
endif() endif()
@ -325,5 +342,3 @@ add_custom_target(GenerateDesktopFile
COMMAND sed 's\#PREFIX\#${CMAKE_INSTALL_PREFIX}\#' ${CMAKE_CURRENT_SOURCE_DIR}/data/supertuxkart_desktop.template | sed 's\#VERSION\#${PROJECT_VERSION}\#' > ${CMAKE_CURRENT_SOURCE_DIR}/data/supertuxkart.desktop COMMAND sed 's\#PREFIX\#${CMAKE_INSTALL_PREFIX}\#' ${CMAKE_CURRENT_SOURCE_DIR}/data/supertuxkart_desktop.template | sed 's\#VERSION\#${PROJECT_VERSION}\#' > ${CMAKE_CURRENT_SOURCE_DIR}/data/supertuxkart.desktop
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
ADD_DEPENDENCIES(supertuxkart GenerateDesktopFile) ADD_DEPENDENCIES(supertuxkart GenerateDesktopFile)

File diff suppressed because one or more lines are too long

View File

@ -9,11 +9,11 @@
</hard> </hard>
<medium> <medium>
<karts number="1"/> <karts number="1"/>
<requirements energy="10" time="105"/> <requirements energy="10" time="65"/>
</medium> </medium>
<easy> <easy>
<karts number="1"/> <karts number="1"/>
<requirements energy="6" time="115"/> <requirements energy="6" time="80"/>
</easy> </easy>
</challenge> </challenge>

View File

@ -7,12 +7,12 @@
y="0.001" y="0.001"
z="-0.003" /> z="-0.003" />
<material file="nitro-particle.png" /> <material file="skid-particle1.png" />
<!-- Amount of particles emitted per second. The minimum rate <!-- Amount of particles emitted per second. The minimum rate
is used to show that the skidding bonus is now available, is used to show that the skidding bonus is now available,
the maximum is used when the skid bonus is applied to the kart --> the maximum is used when the skid bonus is applied to the kart -->
<rate min="50" <rate min="200"
max="600" /> max="600" />
<!-- Minimal and maximal lifetime of a particle, in milliseconds. --> <!-- Minimal and maximal lifetime of a particle, in milliseconds. -->

View File

@ -7,7 +7,7 @@
y="0.001" y="0.001"
z="-0.003" /> z="-0.003" />
<material file="nitro-particle.png" /> <material file="skid-particle2.png" />
<!-- Amount of particles emitted per second. The minimum rate <!-- Amount of particles emitted per second. The minimum rate
is used to show that the skidding bonus is now available, is used to show that the skidding bonus is now available,

View File

@ -6,19 +6,31 @@
<spacer height="2%" width="1"/> <spacer height="2%" width="1"/>
<icon-button id="novice" icon="gui/difficulty_easy.png" <div width="100%" proportion="1" layout="horizontal-row">
I18N="Difficulty" text="Novice" proportion="1"/> <icon-button id="novice" icon="gui/difficulty_easy.png"
I18N="Difficulty" text="Novice" height="100%"/>
<spacer width="5%" height="1"/>
<label id="novice_label" proportion="1" height="100%"/>
</div>
<spacer height="8%" width="1"/> <spacer height="8%" width="1"/>
<icon-button id="intermediate" icon="gui/difficulty_medium.png" <div width="100%" proportion="1" layout="horizontal-row">
I18N="Difficulty" text="Intermediate" proportion="1"/> <icon-button id="intermediate" icon="gui/difficulty_medium.png"
I18N="Difficulty" text="Intermediate" height="100%"/>
<spacer width="5%" height="1"/>
<label id="intermediate_label" proportion="1" height="100%"/>
</div>
<spacer height="8%" width="1"/> <spacer height="8%" width="1"/>
<icon-button id="expert" icon="gui/difficulty_hard.png" <div width="100%" proportion="1" layout="horizontal-row">
I18N="Difficulty" text="Expert" proportion="1"/> <icon-button id="expert" icon="gui/difficulty_hard.png"
I18N="Difficulty" text="Expert" height="100%"/>
<spacer width="5%" height="1"/>
<label id="difficult_label" proportion="1" height="100%"/>
</div>
<spacer height="8%" width="1"/> <spacer height="8%" width="1"/>
</div> </div>

View File

@ -0,0 +1,69 @@
<stkgui>
<div x="5%" y="5%" width="90%" height="90%" layout="vertical-row">
<label id="name" width="100%" text_align="center"/>
<spacer width="1" height="5%"/>
<div width="95%" proportion="5" layout="horizontal-row">
<!-- Left pane -->
<div proportion="1" height="100%" layout="vertical-row">
<label id="highscores" width="100%" text_align="center" text="= Highscores ="/>
<spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore1" icon="gui/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore1" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore2" icon="gui/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore2" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore3" icon="gui/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore3" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" proportion="1"/>
<label id="author" width="100%" text_align="center" word_wrap="true"/>
</div>
<!-- Right pane -->
<div proportion="1" height="100%" layout="vertical-row">
<placeholder proportion="1" height="100%" id="screenshot_div">
</placeholder>
<div width="75%" height="fit" layout="horizontal-row" >
<spacer width="40" height="2" />
<checkbox id="reverse"/>
<spacer width="20" height="2" />
<label height="100%" I18N="Drive the track reverse" text="Reverse"/>
</div>
</div>
</div>
<spacer width="1" height="5%"/>
<spinner id="lapcountspinner" width="50%" min="1" max="20" align="center" warp_around="true"
I18N="In the track setup screen (number of laps choice, where %i is the number)" text="%i laps"/>
<spacer width="1" height="5%"/>
<button id="start" text="Start Race" align="center"/>
<spacer width="1" height="1%"/>
</div>
</stkgui>

BIN
data/models/bronze.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
data/models/silver.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

BIN
data/models/trophy_gold.b3d Normal file

Binary file not shown.

Binary file not shown.

View File

@ -312,7 +312,7 @@
bevelling, and uses a simple box shape. bevelling, and uses a simple box shape.
As an example, a value of 1 for x and z will result in a As an example, a value of 1 for x and z will result in a
sharp 'arrow' like shape. --> sharp 'arrow' like shape. -->
<collision impulse="150" impulse-time="0.1" side-impulse="600" <collision impulse="12000" impulse-time="0.1" side-impulse="600"
restitution="1.0" bevel-factor="0.5 0.0 0.5" /> restitution="1.0" bevel-factor="0.5 0.0 0.5" />
<!-- Kart-specific plunger and rubber band handling: max-length is <!-- Kart-specific plunger and rubber band handling: max-length is

View File

@ -45,19 +45,50 @@ void Challenge::load(const XMLNode* challengesNode)
m_data->getId().c_str()); m_data->getId().c_str());
return; return;
} }
const XMLNode* easy = node->getNode("easy");
// See if the challenge is solved (it's activated later from the const XMLNode* medium = node->getNode("medium");
// unlock_manager). const XMLNode* hard = node->getNode("hard");
bool finished=false;
node->get("solved", &finished); m_state[0] = CH_INACTIVE;
m_state[1] = CH_INACTIVE;
m_state[2] = CH_INACTIVE;
if (easy != NULL)
{
bool finished = false;
easy->get("solved", &finished);
m_state = finished ? CH_SOLVED : CH_INACTIVE; if (finished) m_state[0] = CH_SOLVED;
}
if (medium != NULL)
{
bool finished = false;
medium->get("solved", &finished);
if (finished) m_state[1] = CH_SOLVED;
}
if (hard != NULL)
{
bool finished = false;
hard->get("solved", &finished);
if (finished) m_state[2] = CH_SOLVED;
}
} // load } // load
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const wchar_t* boolstr(bool b)
{
return (b ? L"true" : L"false");
}
void Challenge::save(XMLWriter& writer) void Challenge::save(XMLWriter& writer)
{ {
writer << L" <" << core::stringw(m_data->getId().c_str()) << L" solved=\"" writer << L" <" << core::stringw(m_data->getId().c_str()) << L">\n"
<< (isSolved() ? L"true" : L"false") << L"\""; << L" <easy solved=\"" << boolstr(isSolved(RaceManager::RD_EASY)) << L"\"/>\n"
writer << L" />\n"; << L" <medium solved=\"" << boolstr(isSolved(RaceManager::RD_MEDIUM)) << L"\"/>\n"
<< L" <hard solved=\"" << boolstr(isSolved(RaceManager::RD_HARD)) << L"\"/>\n"
<< L" </" << core::stringw(m_data->getId().c_str()) << L">\n";
} // save } // save

View File

@ -30,6 +30,7 @@
#include <fstream> #include <fstream>
#include <irrString.h> #include <irrString.h>
#include "race/race_manager.hpp"
#include "utils/no_copy.hpp" #include "utils/no_copy.hpp"
#include "utils/translation.hpp" #include "utils/translation.hpp"
@ -46,26 +47,38 @@ class Challenge : public NoCopy
private: private:
enum {CH_INACTIVE, // challenge not yet possible enum {CH_INACTIVE, // challenge not yet possible
CH_ACTIVE, // challenge possible, but not yet solved CH_ACTIVE, // challenge possible, but not yet solved
CH_SOLVED} m_state; // challenge was solved CH_SOLVED} // challenge was solved
m_state[RaceManager::DIFFICULTY_COUNT];
ChallengeData* m_data; ChallengeData* m_data;
public: public:
Challenge(ChallengeData* data) : m_state(CH_INACTIVE) Challenge(ChallengeData* data)
{ m_data = data; } {
m_data = data;
m_state[RaceManager::RD_EASY] = CH_INACTIVE;
m_state[RaceManager::RD_MEDIUM] = CH_INACTIVE;
m_state[RaceManager::RD_HARD] = CH_INACTIVE;
}
virtual ~Challenge() {}; virtual ~Challenge() {};
void load(const XMLNode* config); void load(const XMLNode* config);
void save(XMLWriter& writer); void save(XMLWriter& writer);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool isSolved() const {return m_state==CH_SOLVED; } bool isSolved(RaceManager::Difficulty d) const {return m_state[d]==CH_SOLVED; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool isActive() const {return m_state==CH_ACTIVE; } bool isSolvedAtAnyDifficulty() const {return m_state[0]==CH_SOLVED ||
m_state[1]==CH_SOLVED ||
m_state[2]==CH_SOLVED; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setSolved() {m_state = CH_SOLVED; } bool isActive(RaceManager::Difficulty d) const {return m_state[d]==CH_ACTIVE; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setActive() {m_state = CH_ACTIVE; } void setSolved(RaceManager::Difficulty d) {m_state[d] = CH_SOLVED; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
ChallengeData* getData() { return m_data; } void setActive(RaceManager::Difficulty d) {m_state[d] = CH_ACTIVE; }
// ------------------------------------------------------------------------
ChallengeData* getData() { return m_data; }
const ChallengeData* getData() const { return m_data; }
}; };
#endif #endif

View File

@ -356,12 +356,13 @@ bool ChallengeData::raceFinished()
int d = race_manager->getDifficulty(); int d = race_manager->getDifficulty();
Kart* kart = world->getPlayerKart(0);
if (track_name != m_track_id ) return false; if (track_name != m_track_id ) return false;
if ((int)world->getNumKarts() < m_num_karts[d] ) return false; if ((int)world->getNumKarts() < m_num_karts[d] ) return false;
Kart* kart = world->getPlayerKart(0);
if (m_energy[d] > 0 && kart->getEnergy() < m_energy[d] ) return false; if (m_energy[d] > 0 && kart->getEnergy() < m_energy[d] ) return false;
if (m_position[d] > 0 && kart->getPosition() > m_position[d]) return false; if (m_position[d] > 0 && kart->getPosition() > m_position[d]) return false;
// Follow the leader // Follow the leader
// ----------------- // -----------------
if(m_minor==RaceManager::MINOR_MODE_FOLLOW_LEADER) if(m_minor==RaceManager::MINOR_MODE_FOLLOW_LEADER)

View File

@ -29,35 +29,6 @@ bool GameSlot::isLocked(const std::string& feature)
{ {
return m_locked_features.find(feature)!=m_locked_features.end(); return m_locked_features.find(feature)!=m_locked_features.end();
} // featureIsLocked } // featureIsLocked
//-----------------------------------------------------------------------------
const std::vector<const ChallengeData*> GameSlot::getUnlockedFeatures()
{
std::vector<const ChallengeData*> out;
std::map<std::string, Challenge*>::const_iterator i;
for(i = m_challenges_state.begin();
i != m_challenges_state.end(); i++)
{
if (i->second->isSolved()) out.push_back(i->second->getData());
}
return out;
}
//-----------------------------------------------------------------------------
const std::vector<const ChallengeData*> GameSlot::getLockedChallenges()
{
std::vector<const ChallengeData*> out;
std::map<std::string, Challenge*>::const_iterator i;
for(i = m_challenges_state.begin();
i != m_challenges_state.end(); i++)
{
if (!i->second->isSolved()) out.push_back(i->second->getData());
}
return out;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void GameSlot::computeActive() void GameSlot::computeActive()
@ -70,21 +41,53 @@ void GameSlot::computeActive()
{ {
// Changed challenge // Changed challenge
// ----------------- // -----------------
if((i->second)->isSolved()) if((i->second)->isSolvedAtAnyDifficulty())
{ {
// The constructor calls computeActive, which actually locks // The constructor calls computeActive, which actually locks
// all features, so unlock the solved ones (and don't try to // all features, so unlock the solved ones (and don't try to
// save the state, since we are currently reading it) // save the state, since we are currently reading it)
unlockFeature(i->second, /*save*/ false); if (i->second->isSolved(RaceManager::RD_EASY))
{
unlockFeature(i->second, RaceManager::RD_EASY, /*save*/ false);
}
if (i->second->isSolved(RaceManager::RD_MEDIUM))
{
unlockFeature(i->second, RaceManager::RD_MEDIUM, /*save*/ false);
}
if (i->second->isSolved(RaceManager::RD_HARD))
{
unlockFeature(i->second, RaceManager::RD_HARD, /*save*/ false);
}
m_points++; m_points++;
continue; }
else
{
// Otherwise lock the feature
// --------------------------
lockFeature(i->second);
} }
// Otherwise lock the feature if (i->second->isSolved(RaceManager::RD_HARD))
// -------------------------- {
lockFeature(i->second); // challenge beaten at hardest, nothing more to do here
i->second->setActive(); continue;
}
else if (i->second->isSolved(RaceManager::RD_MEDIUM))
{
i->second->setActive(RaceManager::RD_HARD);
}
else if (i->second->isSolved(RaceManager::RD_EASY))
{
i->second->setActive(RaceManager::RD_HARD);
i->second->setActive(RaceManager::RD_MEDIUM);
}
else
{
i->second->setActive(RaceManager::RD_HARD);
i->second->setActive(RaceManager::RD_MEDIUM);
i->second->setActive(RaceManager::RD_EASY);
}
} // for i } // for i
clearUnlocked(); clearUnlocked();
} // computeActive } // computeActive
@ -102,7 +105,7 @@ void GameSlot::lockFeature(Challenge *challenge)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void GameSlot::unlockFeature(Challenge* c, bool do_save) void GameSlot::unlockFeature(Challenge* c, RaceManager::Difficulty d, bool do_save)
{ {
const unsigned int amount = (unsigned int)c->getData()->getFeatures().size(); const unsigned int amount = (unsigned int)c->getData()->getFeatures().size();
for(unsigned int n=0; n<amount; n++) for(unsigned int n=0; n<amount; n++)
@ -120,7 +123,7 @@ void GameSlot::unlockFeature(Challenge* c, bool do_save)
// Add to list of recently unlocked features // Add to list of recently unlocked features
m_unlocked_features.push_back(c->getData()); m_unlocked_features.push_back(c->getData());
c->setSolved(); // reset isActive flag c->setSolved(d); // reset isActive flag
// Save the new unlock information // Save the new unlock information
if(do_save) unlock_manager->save(); if(do_save) unlock_manager->save();
@ -128,24 +131,6 @@ void GameSlot::unlockFeature(Challenge* c, bool do_save)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
std::vector<const ChallengeData*> GameSlot::getActiveChallenges()
{
computeActive();
std::vector<const ChallengeData*> out;
std::map<std::string, Challenge*>::const_iterator i;
for(i = m_challenges_state.begin();
i != m_challenges_state.end(); i++)
{
if (i->second->isActive()) out.push_back(i->second->getData());
}
return out;
} // getActiveChallenges
//-----------------------------------------------------------------------------
/** This is called when a race is finished. Call all active challenges /** This is called when a race is finished. Call all active challenges
*/ */
void GameSlot::raceFinished() void GameSlot::raceFinished()
@ -156,9 +141,9 @@ void GameSlot::raceFinished()
for(i = m_challenges_state.begin(); for(i = m_challenges_state.begin();
i != m_challenges_state.end(); i++) i != m_challenges_state.end(); i++)
{ {
if(i->second->isActive() && i->second->getData()->raceFinished()) if(i->second->isActive(race_manager->getDifficulty()) && i->second->getData()->raceFinished())
{ {
unlockFeature(i->second); unlockFeature(i->second, race_manager->getDifficulty());
} // if isActive && challenge solved } // if isActive && challenge solved
} }
//race_manager->setCoinTarget(0); //reset //race_manager->setCoinTarget(0); //reset
@ -172,10 +157,11 @@ void GameSlot::grandPrixFinished()
for(i = m_challenges_state.begin(); for(i = m_challenges_state.begin();
i != m_challenges_state.end(); i++) i != m_challenges_state.end(); i++)
{ {
if(i->second->isActive() && i->second->getData()->grandPrixFinished()) if(i->second->isActive(race_manager->getDifficulty()) &&
i->second->getData()->grandPrixFinished())
{ {
printf("===== A FEATURE WAS UNLOCKED BECAUSE YOU WON THE GP!! ==\n"); printf("===== A FEATURE WAS UNLOCKED BECAUSE YOU WON THE GP!! ==\n");
unlockFeature(i->second); unlockFeature(i->second, race_manager->getDifficulty());
} }
} }
race_manager->setCoinTarget(0); race_manager->setCoinTarget(0);

View File

@ -25,6 +25,8 @@
#include <vector> #include <vector>
#include <irrString.h> #include <irrString.h>
#include "race/race_manager.hpp"
class ChallengeData; class ChallengeData;
class Challenge; class Challenge;
class XMLWriter; class XMLWriter;
@ -70,26 +72,17 @@ public:
/** Returns the list of recently unlocked features (e.g. call at the end of a /** Returns the list of recently unlocked features (e.g. call at the end of a
race to know if any features were unlocked) */ race to know if any features were unlocked) */
const std::vector<const ChallengeData*> const std::vector<const ChallengeData*>
getRecentlyUnlockedFeatures() {return m_unlocked_features;} getRecentlyCompletedChallenges() {return m_unlocked_features;}
/** Clear the list of recently unlocked challenges */ /** Clear the list of recently unlocked challenges */
void clearUnlocked () {m_unlocked_features.clear(); } void clearUnlocked () {m_unlocked_features.clear(); }
/** Returns a complete list of all solved challenges */
const std::vector<const ChallengeData*> getUnlockedFeatures();
/** Returns the list of currently inaccessible (locked) challenges */
const std::vector<const ChallengeData*> getLockedChallenges();
bool isLocked (const std::string& feature); bool isLocked (const std::string& feature);
void lockFeature (Challenge *challenge); void lockFeature (Challenge *challenge);
void unlockFeature (Challenge* c, bool do_save=true); void unlockFeature (Challenge* c, RaceManager::Difficulty d, bool do_save=true);
std::vector<const ChallengeData*> getActiveChallenges();
void raceFinished (); void raceFinished ();
void grandPrixFinished (); void grandPrixFinished ();

View File

@ -63,6 +63,7 @@ void LODNode::render()
void LODNode::OnRegisterSceneNode() void LODNode::OnRegisterSceneNode()
{ {
if (!isVisible()) return; if (!isVisible()) return;
if (m_nodes.size() == 0) return;
// TODO: optimize this, there is no need to check every frame // TODO: optimize this, there is no need to check every frame
scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera(); scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera();

View File

@ -92,7 +92,11 @@ public:
void add(int level, scene::ISceneNode* node, bool reparent); void add(int level, scene::ISceneNode* node, bool reparent);
/** Get the highest level of detail node */ /** Get the highest level of detail node */
scene::ISceneNode* getFirstNode() { assert(m_nodes.size() > 0); return m_nodes[0]; } scene::ISceneNode* getFirstNode()
{
if (m_nodes.size() > 0) return m_nodes[0];
else return NULL;
}
std::vector<scene::ISceneNode*>& getAllNodes() { return m_nodes; } std::vector<scene::ISceneNode*>& getAllNodes() { return m_nodes; }

View File

@ -75,28 +75,34 @@ public:
class WaterShaderProvider : public video::IShaderConstantSetCallBack class WaterShaderProvider : public video::IShaderConstantSetCallBack
{ {
float m_dx_1, m_dy_1, m_dx_2, m_dy_2; 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;
public: public:
LEAK_CHECK() LEAK_CHECK()
WaterShaderProvider() WaterShaderProvider(float water_shader_speed_1,
float water_shader_speed_2)
{ {
m_dx_1 = 0.0f; m_dx_1 = 0.0f;
m_dy_1 = 0.0f; m_dy_1 = 0.0f;
m_dx_2 = 0.0f; m_dx_2 = 0.0f;
m_dy_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;
} }
virtual void OnSetConstants( virtual void OnSetConstants(
irr::video::IMaterialRendererServices *services, irr::video::IMaterialRendererServices *services,
s32 userData) s32 userData)
{ {
m_dx_1 += GUIEngine::getLatestDt()/15.0f; m_dx_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1;
m_dy_1 += GUIEngine::getLatestDt()/15.0f; m_dy_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1;
m_dx_2 += GUIEngine::getLatestDt()/25.0f; m_dx_2 += GUIEngine::getLatestDt()*m_water_shader_speed_2;
m_dy_2 -= GUIEngine::getLatestDt()/25.0f; m_dy_2 -= GUIEngine::getLatestDt()*m_water_shader_speed_2;
if (m_dx_1 > 1.0f) m_dx_1 -= 1.0f; if (m_dx_1 > 1.0f) m_dx_1 -= 1.0f;
if (m_dy_1 > 1.0f) m_dy_1 -= 1.0f; if (m_dy_1 > 1.0f) m_dy_1 -= 1.0f;
@ -429,6 +435,11 @@ Material::Material(const XMLNode *node, int index)
} }
node->get("water-shader", &m_water_shader); node->get("water-shader", &m_water_shader);
if (m_water_shader)
{
node->get("water-shader-speed-1", &m_water_shader_speed_1);
node->get("water-shader-speed-2", &m_water_shader_speed_2);
}
// Terrain-specifc sound effect // Terrain-specifc sound effect
const unsigned int children_count = node->getNumNodes(); const unsigned int children_count = node->getNumNodes();
@ -505,6 +516,8 @@ void Material::init(unsigned int index)
m_add = false; m_add = false;
m_disable_z_write = false; m_disable_z_write = false;
m_water_shader = false; m_water_shader = false;
m_water_shader_speed_1 = 6.6667f;
m_water_shader_speed_2 = 4.0f;
m_fog = true; m_fog = true;
m_max_speed_fraction = 1.0f; m_max_speed_fraction = 1.0f;
m_slowdown_time = 1.0f; m_slowdown_time = 1.0f;
@ -987,7 +1000,8 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
{ {
if (m_shaders[WATER_SHADER] == NULL) if (m_shaders[WATER_SHADER] == NULL)
{ {
m_shaders[WATER_SHADER] = new WaterShaderProvider(); m_shaders[WATER_SHADER] = 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(1, irr_driver->getTexture(file_manager->getTextureFile("waternormals.jpg")));

View File

@ -107,7 +107,18 @@ private:
bool m_zipper; bool m_zipper;
/** If a kart is rescued when driving on this surface. */ /** If a kart is rescued when driving on this surface. */
bool m_drive_reset; bool m_drive_reset;
/** If the water shader (simulating wave effects and reflexions) is enabled */
bool m_water_shader; bool m_water_shader;
/** Speed of the 'main' wave in the water shader. Only used if
m_water_shader is true */
float m_water_shader_speed_1;
/** Speed of the 'secondary' waves in the water shader. Only used if
m_water_shader is true */
float m_water_shader_speed_2;
/** If a kart is rescued when crashing into this surface. */ /** If a kart is rescued when crashing into this surface. */
CollisionReaction m_collision_reaction; CollisionReaction m_collision_reaction;

View File

@ -248,6 +248,7 @@ void AbstractStateManager::resetAndGoToScreen(Screen* screen)
if (m_game_mode != GAME) getCurrentScreen()->tearDown(); if (m_game_mode != GAME) getCurrentScreen()->tearDown();
m_menu_stack.clear(); m_menu_stack.clear();
if (!screen->isLoaded()) screen->loadFromFile();
m_menu_stack.push_back(name); m_menu_stack.push_back(name);
setGameState(MENU); setGameState(MENU);

View File

@ -372,6 +372,9 @@ namespace GUIEngine
Another possible value is "fit", which will make a \<div\> fit to its Another possible value is "fit", which will make a \<div\> fit to its
contents. contents.
Another possible value is "font", which will use the size of the font
(useful to insert widgets inside text)
\n \n
\subsection prop9 PROP_MAX_WIDTH, PROP_MAX_HEIGHT \subsection prop9 PROP_MAX_WIDTH, PROP_MAX_HEIGHT
<em> Names in XML files: </em> \c "max_width", \c "max_height" <em> Names in XML files: </em> \c "max_width", \c "max_height"
@ -414,6 +417,10 @@ namespace GUIEngine
is not what you're looking for; instead, add a stretching spacer before is not what you're looking for; instead, add a stretching spacer before
and after the widget(s) you want to center. and after the widget(s) you want to center.
\note When applied to a label widget, this property will center the text
widget within its parent. To align the text inside the label widget,
see \ref prop4
\n \n
\subsection prop13 PROP_PROPORTION \subsection prop13 PROP_PROPORTION
<em> Name in XML files: </em> \c "proportion" <em> Name in XML files: </em> \c "proportion"

View File

@ -259,7 +259,8 @@ void LayoutManager::readCoords(Widget* self)
// width // width
{ {
int abs_w = -1, percent_w = -1; int abs_w = -1, percent_w = -1;
if (convertToCoord(width, &abs_w, &percent_w )) if (width == "font") self->m_absolute_w = GUIEngine::getFontHeight();
else if (convertToCoord(width, &abs_w, &percent_w ))
{ {
if (abs_w > -1) self->m_absolute_w = abs_w; if (abs_w > -1) self->m_absolute_w = abs_w;
else if (percent_w > -1) self->m_relative_w = (float)percent_w; else if (percent_w > -1) self->m_relative_w = (float)percent_w;
@ -271,7 +272,8 @@ void LayoutManager::readCoords(Widget* self)
// height // height
{ {
int abs_h = -1, percent_h = -1; int abs_h = -1, percent_h = -1;
if (convertToCoord(height, &abs_h, &percent_h )) if (height == "font") self->m_absolute_h = GUIEngine::getFontHeight();
else if (convertToCoord(height, &abs_h, &percent_h ))
{ {
if (abs_h > -1) self->m_absolute_h = abs_h; if (abs_h > -1) self->m_absolute_h = abs_h;
else if (percent_h > -1) self->m_relative_h = (float)percent_h; else if (percent_h > -1) self->m_relative_h = (float)percent_h;

View File

@ -153,6 +153,8 @@ namespace GUIEngine
*/ */
int getMin() const { return m_min; } int getMin() const { return m_min; }
void setMin(int n) { m_min = n; }
/** Override method from base class Widget */ /** Override method from base class Widget */
virtual void setActivated(); virtual void setActivated();

View File

@ -292,14 +292,52 @@ void Attachment::hitBanana(Item *item, int new_attachment)
} // hitBanana } // hitBanana
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//** Moves a bomb from kart FROM to kart TO. /** Updates the attachments in case of a kart-kart collision. This must only
void Attachment::moveBombFromTo(Kart *from, Kart *to) * be called for one of the karts in the collision, since it will update
* the attachment for both karts.
* \param other Pointer to the other kart hit.
*/
void Attachment::handleCollisionWithKart(Kart *other)
{ {
to->getAttachment()->set(ATTACH_BOMB, Attachment *attachment_other=other->getAttachment();
from->getAttachment()->getTimeLeft()+
stk_config->m_bomb_time_increase, from); if(getType()==Attachment::ATTACH_BOMB)
from->getAttachment()->clear(); {
} // moveBombFromTo // If both karts have a bomb, explode them immediately:
if(attachment_other->getType()==Attachment::ATTACH_BOMB)
{
setTimeLeft(0.0f);
attachment_other->setTimeLeft(0.0f);
}
else // only this kart has a bomb, move it to the other
{
if(getPreviousOwner()!=other)
{
// Don't move if this bomb was from other kart originally
other->getAttachment()->set(ATTACH_BOMB,
getTimeLeft()+
stk_config->m_bomb_time_increase,
m_kart);
other->playCustomSFX(SFXManager::CUSTOM_ATTACH);
clear();
}
}
} // type==BOMB
else if(attachment_other->getType()==Attachment::ATTACH_BOMB &&
attachment_other->getPreviousOwner()!=m_kart)
{
set(ATTACH_BOMB, other->getAttachment()->getTimeLeft()+
stk_config->m_bomb_time_increase, other);
other->getAttachment()->clear();
m_kart->playCustomSFX(SFXManager::CUSTOM_ATTACH);
}
else
{
m_kart->playCustomSFX(SFXManager::CUSTOM_CRASH);
other->playCustomSFX(SFXManager::CUSTOM_CRASH);
}
} // handleCollisionWithKart
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void Attachment::update(float dt) void Attachment::update(float dt)

View File

@ -99,9 +99,9 @@ public:
void clear (); void clear ();
void hitBanana(Item *item, int new_attachment=-1); void hitBanana(Item *item, int new_attachment=-1);
void update (float dt); void update (float dt);
void moveBombFromTo(Kart *from, Kart *to); void handleCollisionWithKart(Kart *other);
void set (AttachmentType type, float time, Kart *previous_kart=NULL); void set (AttachmentType type, float time, Kart *previous_kart=NULL);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the type of the attachment, but keeps the old time left value. */ /** Sets the type of the attachment, but keeps the old time left value. */
void set (AttachmentType type) { set(type, m_time_left); } void set (AttachmentType type) { set(type, m_time_left); }

View File

@ -22,6 +22,7 @@
#include "audio/sfx_manager.hpp" #include "audio/sfx_manager.hpp"
#include "config/user_config.hpp" #include "config/user_config.hpp"
#include "config/stk_config.hpp" #include "config/stk_config.hpp"
#include "items/attachment.hpp"
#include "items/item_manager.hpp" #include "items/item_manager.hpp"
#include "items/projectile_manager.hpp" #include "items/projectile_manager.hpp"
#include "karts/kart.hpp" #include "karts/kart.hpp"

View File

@ -20,6 +20,7 @@
#include "audio/sfx_base.hpp" #include "audio/sfx_base.hpp"
#include "audio/sfx_manager.hpp" #include "audio/sfx_manager.hpp"
#include "items/attachment.hpp"
#include "items/projectile_manager.hpp" #include "items/projectile_manager.hpp"
#include "karts/kart.hpp" #include "karts/kart.hpp"
#include "modes/linear_world.hpp" #include "modes/linear_world.hpp"

View File

@ -39,6 +39,7 @@
# include "graphics/irr_driver.hpp" # include "graphics/irr_driver.hpp"
#endif #endif
#include "graphics/slip_stream.hpp" #include "graphics/slip_stream.hpp"
#include "items/attachment.hpp"
#include "modes/linear_world.hpp" #include "modes/linear_world.hpp"
#include "network/network_manager.hpp" #include "network/network_manager.hpp"
#include "race/race_manager.hpp" #include "race/race_manager.hpp"
@ -264,6 +265,7 @@ void DefaultAIController::update(float dt)
/*And obviously general kart stuff*/ /*And obviously general kart stuff*/
AIBaseController::update(dt); AIBaseController::update(dt);
m_collided = false; m_collided = false;
m_controls->m_fire = false;
} // update } // update
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -38,6 +38,7 @@
#ifdef AI_DEBUG #ifdef AI_DEBUG
#include "graphics/irr_driver.hpp" #include "graphics/irr_driver.hpp"
#endif #endif
#include "items/attachment.hpp"
#include "modes/linear_world.hpp" #include "modes/linear_world.hpp"
#include "network/network_manager.hpp" #include "network/network_manager.hpp"
#include "race/race_manager.hpp" #include "race/race_manager.hpp"

View File

@ -25,6 +25,7 @@
#include "graphics/camera.hpp" #include "graphics/camera.hpp"
#include "graphics/irr_driver.hpp" #include "graphics/irr_driver.hpp"
#include "input/input_manager.hpp" #include "input/input_manager.hpp"
#include "items/attachment.hpp"
#include "items/item.hpp" #include "items/item.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "race/history.hpp" #include "race/history.hpp"

View File

@ -21,6 +21,7 @@
#include "graphics/camera.hpp" #include "graphics/camera.hpp"
#include "graphics/referee.hpp" #include "graphics/referee.hpp"
#include "graphics/stars.hpp" #include "graphics/stars.hpp"
#include "items/attachment.hpp"
#include "karts/kart.hpp" #include "karts/kart.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "modes/three_strikes_battle.hpp" #include "modes/three_strikes_battle.hpp"

View File

@ -42,6 +42,7 @@
#include "karts/kart_gfx.hpp" #include "karts/kart_gfx.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "items/attachment.hpp"
#include "items/item_manager.hpp" #include "items/item_manager.hpp"
#include "karts/controller/end_controller.hpp" #include "karts/controller/end_controller.hpp"
#include "karts/kart_model.hpp" #include "karts/kart_model.hpp"
@ -69,7 +70,8 @@
* \param is_first_kart Indicates whether this is the first *player* kart * \param is_first_kart Indicates whether this is the first *player* kart
* \param init_transform The initial position and rotation for this kart. * \param init_transform The initial position and rotation for this kart.
*/ */
Kart::Kart (const std::string& ident, Track* track, int position, bool is_first_kart, Kart::Kart (const std::string& ident, unsigned int world_kart_id,
Track* track, int position, bool is_first_kart,
const btTransform& init_transform, RaceManager::KartType type) const btTransform& init_transform, RaceManager::KartType type)
: TerrainInfo(1), : TerrainInfo(1),
Moveable(), EmergencyAnimation(this), MaxSpeed(this), m_powerup(this) Moveable(), EmergencyAnimation(this), MaxSpeed(this), m_powerup(this)
@ -91,16 +93,15 @@ Kart::Kart (const std::string& ident, Track* track, int position, bool is_first_
m_kart_model = m_kart_properties->getKartModelCopy(); m_kart_model = m_kart_properties->getKartModelCopy();
m_initial_position = position; m_initial_position = position;
m_race_position = position; m_race_position = position;
m_world_kart_id = world_kart_id;
m_collected_energy = 0; m_collected_energy = 0;
m_finished_race = false; m_finished_race = false;
m_wheel_toggle = 1;
m_finish_time = 0.0f; m_finish_time = 0.0f;
m_bubblegum_time = 0.0f; m_bubblegum_time = 0.0f;
m_invulnerable_time = 0.0f; m_invulnerable_time = 0.0f;
m_squash_time = 0.0f; m_squash_time = 0.0f;
m_shadow_enabled = false; m_shadow_enabled = false;
m_shadow = NULL; m_shadow = NULL;
m_terrain_particles = NULL;
m_collision_particles = NULL; m_collision_particles = NULL;
m_slipstream = NULL; m_slipstream = NULL;
m_skidmarks = NULL; m_skidmarks = NULL;
@ -430,7 +431,6 @@ Kart::~Kart()
delete m_kart_gfx; delete m_kart_gfx;
if(m_terrain_sound) sfx_manager->deleteSFX(m_terrain_sound); if(m_terrain_sound) sfx_manager->deleteSFX(m_terrain_sound);
if(m_previous_terrain_sound) sfx_manager->deleteSFX(m_previous_terrain_sound); if(m_previous_terrain_sound) sfx_manager->deleteSFX(m_previous_terrain_sound);
if(m_terrain_particles) delete m_terrain_particles;
if(m_collision_particles) delete m_collision_particles; if(m_collision_particles) delete m_collision_particles;
if(m_slipstream) delete m_slipstream; if(m_slipstream) delete m_slipstream;
if(m_rain) delete m_rain; if(m_rain) delete m_rain;
@ -906,7 +906,6 @@ void Kart::update(float dt)
//smoke drawing control point //smoke drawing control point
if (UserConfigParams::m_graphical_effects) if (UserConfigParams::m_graphical_effects)
{ {
if (m_terrain_particles) m_terrain_particles->update(dt);
if (m_rain) if (m_rain)
{ {
m_rain->setPosition( getCamera()->getCameraSceneNode()->getPosition() ); m_rain->setPosition( getCamera()->getCameraSceneNode()->getPosition() );
@ -1153,140 +1152,92 @@ void Kart::handleMaterialGFX()
// on top of a surface (i.e. not falling), actually touching // on top of a surface (i.e. not falling), actually touching
// something with the wheels, and the material has not the // something with the wheels, and the material has not the
// below surface property set. // below surface property set.
if (material && isOnGround() && !material->isBelowSurface() && m_kart_mode != EA_RESCUE) if (material && isOnGround() && !material->isBelowSurface() &&
m_kart_mode != EA_RESCUE && UserConfigParams::m_graphical_effects)
{ {
if (m_terrain_particles)
// Get the appropriate particle data depending on
// wether the kart is skidding or driving.
const ParticleKind* pk =
material->getParticlesWhen(m_skidding > 1.0f
? Material::EMIT_ON_SKID
: Material::EMIT_ON_DRIVE);
if(!pk)
{ {
Vec3 xyz; // Disable potentially running particle effects
m_wheel_toggle = 1 - m_wheel_toggle; m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
const btWheelInfo &wi = return; // no particle effect, return
getVehicle()->getWheelInfo(2 + m_wheel_toggle);
xyz = wi.m_raycastInfo.m_contactPointWS;
// FIXME: the X position is not yet always accurate.
xyz += Vec3(0.06f * (m_wheel_toggle ? +1 : -1),
0,
0.06f);
// Get the appropriate particle data depending on
// wether the kart is skidding or driving.
const ParticleKind* pk =
material->getParticlesWhen(m_skidding > 1.0f
? Material::EMIT_ON_SKID
: Material::EMIT_ON_DRIVE);
if(!pk)
{
// Disable potentially running particle effects
m_terrain_particles->setCreationRateAbsolute(0);
return; // no particle effect, return
}
// Now compute the particle creation rate:
float rate = 0;
const float speed = fabsf(getSpeed());
if (m_skidding > 1.0f)
{
rate = fabsf(m_controls.m_steer) > 0.8 ? m_skidding - 1 : 0;
}
else if (speed >= 0.5f)
{
rate = speed/m_kart_properties->getMaxSpeed();
}
else
{
m_terrain_particles->setCreationRateAbsolute(0);
return;
}
float create = pk->getMinRate()*(1-rate) + pk->getMaxRate()*rate;
m_terrain_particles->setParticleType(pk);
// when particle type changes, the emitter is re-created at (0,0,0) so we need to
// set the position after setParticleType
m_terrain_particles->setPosition(xyz);
m_terrain_particles->setCreationRateAbsolute(create);
} }
m_kart_gfx->updateTerrain(pk);
return; return;
} }
// Now the kart is either falling, or driving on a terrain which // Now the kart is either falling, or driving on a terrain which
// has the 'below surface' flag set. Detect if there is a surface // has the 'below surface' flag set. Detect if there is a surface
// on top of the kart. // on top of the kart.
// --------------------------------------------------------------
if (m_camera && m_camera->getMode() != Camera::CM_FINAL) if (m_camera && m_camera->getMode() != Camera::CM_FINAL)
{ {
if (material && material->hasFallingEffect() && !m_flying) if (material && material->hasFallingEffect() && !m_flying)
{ {
m_camera->setMode(Camera::CM_FALLING); m_camera->setMode(Camera::CM_FALLING);
} }
else if (m_camera->getMode() != Camera::CM_NORMAL && m_camera->getMode() != Camera::CM_REVERSE) else if (m_camera->getMode() != Camera::CM_NORMAL &&
m_camera->getMode() != Camera::CM_REVERSE)
{ {
m_camera->setMode(Camera::CM_NORMAL); m_camera->setMode(Camera::CM_NORMAL);
} }
} } // camera != final camera
if (m_terrain_particles) if (!UserConfigParams::m_graphical_effects)
{ return;
// Use the middle of the contact points of the two rear wheels
// as the point from which to cast the ray upwards
const btWheelInfo::RaycastInfo &ri2 =
getVehicle()->getWheelInfo(2).m_raycastInfo;
const btWheelInfo::RaycastInfo &ri3 =
getVehicle()->getWheelInfo(3).m_raycastInfo;
Vec3 from = (ri2.m_contactPointWS + ri3.m_contactPointWS)*0.5f;
Vec3 xyz;
const Material *surface_material;
if(!getSurfaceInfo(from, &xyz, &surface_material))
{
m_terrain_particles->setCreationRateAbsolute(0);
return;
}
const ParticleKind *pk =
surface_material->getParticlesWhen(Material::EMIT_ON_DRIVE);
if(pk && !m_flying && m_kart_mode != EA_RESCUE)
{
const float distance = xyz.distance2(from);
m_terrain_particles->setParticleType(pk);
m_terrain_particles->setPosition(xyz.toIrrVector());
//const float speed = fabsf(getSpeed());
//float rate = (speed>=0.5f) ? speed/m_kart_properties->getMaxSpeed()
// : 0;
float create; // Use the middle of the contact points of the two rear wheels
if (distance < 2.0f) // as the point from which to cast the ray upwards
{ const btWheelInfo::RaycastInfo &ri2 =
create = (float)pk->getMaxRate(); getVehicle()->getWheelInfo(2).m_raycastInfo;
} const btWheelInfo::RaycastInfo &ri3 =
else if (distance < 4.0f) getVehicle()->getWheelInfo(3).m_raycastInfo;
{ Vec3 from = (ri2.m_contactPointWS + ri3.m_contactPointWS)*0.5f;
create = pk->getMinRate() + (pk->getMaxRate() - pk->getMinRate())*(distance - 2.0f)/2.0f; Vec3 xyz;
} const Material *surface_material;
else if(!getSurfaceInfo(from, &xyz, &surface_material))
{ {
create = 0.0f; m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
} return;
m_terrain_particles->setCreationRateAbsolute(create); }
const ParticleKind *pk =
surface_material->getParticlesWhen(Material::EMIT_ON_DRIVE);
const std::string s = surface_material->getSFXName();
if (s != "" && m_kart_mode != EA_RESCUE && if(!pk || m_flying || m_kart_mode == EA_RESCUE)
(m_terrain_sound == NULL || m_terrain_sound->getStatus() == SFXManager::SFX_STOPPED)) return;
{
if (m_previous_terrain_sound) sfx_manager->deleteSFX(m_previous_terrain_sound); // Now the kart is under a surface, and there is a surface effect
m_previous_terrain_sound = m_terrain_sound; // --------------------------------------------------------------
if(m_previous_terrain_sound) m_kart_gfx->setParticleKind(KartGFX::KGFX_TERRAIN, pk);
m_previous_terrain_sound->setLoop(false); m_kart_gfx->setXYZ(KartGFX::KGFX_TERRAIN, xyz);
m_terrain_sound = sfx_manager->createSoundSource(s); const float distance = xyz.distance2(from);
m_terrain_sound->play(); float ratio;
m_terrain_sound->setLoop(false); if (distance < 2.0f) ratio = 1.0f;
} else if (distance < 4.0f) ratio = (4.0f-distance)*0.5f;
else ratio = -1.0f; // No more particles
// handleMaterialSFX(surface_material); m_kart_gfx->setCreationRateRelative(KartGFX::KGFX_TERRAIN, ratio);
}
// Play special sound effects for this terrain
// -------------------------------------------
const std::string s = surface_material->getSFXName();
if (s != "" && m_kart_mode != EA_RESCUE &&
(m_terrain_sound == NULL || m_terrain_sound->getStatus() == SFXManager::SFX_STOPPED))
{
if (m_previous_terrain_sound) sfx_manager->deleteSFX(m_previous_terrain_sound);
m_previous_terrain_sound = m_terrain_sound;
if(m_previous_terrain_sound)
m_previous_terrain_sound->setLoop(false);
m_terrain_sound = sfx_manager->createSoundSource(s);
m_terrain_sound->play();
m_terrain_sound->setLoop(false);
} }
} // handleMaterialGFX } // handleMaterialGFX
@ -1313,7 +1264,7 @@ void Kart::setCamera(Camera *camera)
{ {
m_rain->setCamera( camera->getCameraSceneNode() ); m_rain->setCamera( camera->getCameraSceneNode() );
} }
} } // setCamera
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Sets zipper time, and apply one time additional speed boost. It can be /** Sets zipper time, and apply one time additional speed boost. It can be
@ -1406,44 +1357,64 @@ void Kart::setSlipstreamEffect(float f)
} // setSlipstreamEffect } // setSlipstreamEffect
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/** Called when the kart crashes against the track (k=NULL) or another kart. /** Called when the kart crashes against another kart.
* \param k Either a kart if a kart was hit, or NULL if the track was hit. * \param k The kart that was hit.
* \param m * \param update_attachments If true the attachment of this kart and the
* other kart hit will be updated (e.g. bombs will be moved)
*/ */
void Kart::crashed(Kart *k, const Material *m) void Kart::crashed(Kart *k, bool update_attachments)
{
if(update_attachments)
{
assert(k);
getAttachment()->handleCollisionWithKart(k);
}
crashed();
} // crashed(Kart, update_attachments
// -----------------------------------------------------------------------------
/** Kart hits the track with a given material.
* \param m Material hit, can be NULL if no specific material exists.
*/
void Kart::crashed(const Material *m)
{ {
#ifdef DEBUG #ifdef DEBUG
// Simple debug output for people playing without sound. // Simple debug output for people playing without sound.
// This makes it easier to see if a kart hit the track (esp. // This makes it easier to see if a kart hit the track (esp.
// after a jump). // after a jump).
// FIXME: This should be removed once the physics are fixed. // FIXME: This should be removed once the physics are fixed.
if(!k && UserConfigParams::m_physics_debug) if(UserConfigParams::m_physics_debug)
{ {
// Add a counter to make it easier to see if a new line of // Add a counter to make it easier to see if a new line of
// output was added. // output was added.
static int counter=0; static int counter=0;
printf("Kart %s hit track: %d.\n", getIdent().c_str(), counter++); printf("Kart %s hit track: %d material %s.\n",
getIdent().c_str(), counter++,
m->getTexFname().c_str());
} }
#endif #endif
m_controller->crashed();
/** If a kart is crashing against the track, the collision is often /** If a kart is crashing against the track, the collision is often
* reported more than once, resulting in a machine gun effect, and too * reported more than once, resulting in a machine gun effect, and too
* long disabling of the engine. Therefore, this reaction is disabled * long disabling of the engine. Therefore, this reaction is disabled
* for 0.5 seconds after a crash. * for 0.5 seconds after a crash.
*/ */
if(m && m->getCollisionReaction() != Material::NORMAL && !playingEmergencyAnimation()) if(m && m->getCollisionReaction() != Material::NORMAL &&
!playingEmergencyAnimation())
{ {
std::string particles = m->getCrashResetParticles(); std::string particles = m->getCrashResetParticles();
if (particles.size() > 0) if (particles.size() > 0)
{ {
ParticleKind* kind = ParticleKindManager::get()->getParticles(particles); ParticleKind* kind =
ParticleKindManager::get()->getParticles(particles);
if (kind != NULL) if (kind != NULL)
{ {
if (m_collision_particles == NULL) if (m_collision_particles == NULL)
{ {
Vec3 position(-getKartWidth()*0.35f, 0.06f, getKartLength()*0.5f); Vec3 position(-getKartWidth()*0.35f, 0.06f,
m_collision_particles = new ParticleEmitter(kind, position, getNode()); getKartLength()*0.5f);
m_collision_particles =
new ParticleEmitter(kind, position, getNode());
} }
else else
{ {
@ -1465,14 +1436,24 @@ void Kart::crashed(Kart *k, const Material *m)
{ {
if (m_bounce_back_time <= 0.0f) if (m_bounce_back_time <= 0.0f)
{ {
btVector3 push = m_vehicle->getRigidBody()->getLinearVelocity().normalized(); btVector3 push = m_body->getLinearVelocity().normalized();
push[1] = 0.1f; push[1] = 0.1f;
m_vehicle->getRigidBody()->applyCentralImpulse( -4000.0f*push ); m_body->applyCentralImpulse( -4000.0f*push );
//m_vehicle->getRigidBody()->setLinearVelocity( -m_vehicle->getRigidBody()->getLinearVelocity() ); m_bounce_back_time = 2.0f;
m_bounce_back_time = 2.0f;
} }
} }
} }
crashed();
} // crashed(Material)
// -----------------------------------------------------------------------------
/** Common code used when a kart or a material was hit.
*/
void Kart::crashed()
{
m_controller->crashed();
if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return; if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return;
m_time_last_crash = World::getWorld()->getTime(); m_time_last_crash = World::getWorld()->getTime();
@ -2028,25 +2009,6 @@ void Kart::loadData(RaceManager::KartType type, bool is_first_kart,
createPhysics(); createPhysics();
// Attach Particle System // Attach Particle System
if (UserConfigParams::m_graphical_effects && !isWheeless())
{
try
{
// Note: the smoke system is NOT child of the kart, since bullet
// gives the position of the wheels on the ground in world coordinates.
// So it's easier not to move the particle system with the kart, and set
// the position directly from the wheel coordinates.
Vec3 position(-getKartWidth()*0.35f, 0.06f, -getKartLength()*0.5f);
m_terrain_particles = new ParticleEmitter(ParticleKindManager::get()->getParticles("smoke.xml"),
position);
}
catch (std::runtime_error& e)
{
std::cerr << "[Kart::loadData] " << e.what() << std::endl;
}
}
if (type == RaceManager::KT_PLAYER && UserConfigParams::m_weather_effects && if (type == RaceManager::KT_PLAYER && UserConfigParams::m_weather_effects &&
track->getSkyParticles() != NULL) track->getSkyParticles() != NULL)

View File

@ -28,7 +28,6 @@
#include "LinearMath/btTransform.h" #include "LinearMath/btTransform.h"
#include "items/attachment.hpp"
#include "items/powerup.hpp" #include "items/powerup.hpp"
#include "karts/controller/controller.hpp" #include "karts/controller/controller.hpp"
#include "karts/controller/kart_control.hpp" #include "karts/controller/kart_control.hpp"
@ -42,6 +41,7 @@
class btKart; class btKart;
class btUprightConstraint; class btUprightConstraint;
class Attachment;
class Camera; class Camera;
class Item; class Item;
class KartGFX; class KartGFX;
@ -114,9 +114,6 @@ private:
* determine startup boost. */ * determine startup boost. */
bool m_has_started; bool m_has_started;
/** For skidding smoke */
int m_wheel_toggle;
/**<Maximum engine rpm's for the current gear*/ /**<Maximum engine rpm's for the current gear*/
float m_max_gear_rpm; float m_max_gear_rpm;
@ -154,9 +151,6 @@ private:
* stuck to the kart, i.e. the shadow would be flying, too). */ * stuck to the kart, i.e. the shadow would be flying, too). */
bool m_shadow_enabled; bool m_shadow_enabled;
/** Particle emitter used for terrain-specific effects (including but not limited too skidding). */
ParticleEmitter *m_terrain_particles;
ParticleEmitter *m_sky_particles_emitter; ParticleEmitter *m_sky_particles_emitter;
/** All particle effects. */ /** All particle effects. */
@ -211,6 +205,7 @@ private:
void updateEngineSFX(); void updateEngineSFX();
float getVisualSkidOffset() const; float getVisualSkidOffset() const;
void crashed();
protected: protected:
const KartProperties *m_kart_properties; const KartProperties *m_kart_properties;
@ -220,11 +215,10 @@ protected:
KartModel* m_kart_model; KartModel* m_kart_model;
public: public:
Kart(const std::string& ident, Track* track, int position, bool is_first_kart, Kart(const std::string& ident, unsigned int world_kart_id,
Track* track, int position, bool is_first_kart,
const btTransform& init_transform, RaceManager::KartType type); const btTransform& init_transform, RaceManager::KartType type);
virtual ~Kart(); virtual ~Kart();
unsigned int getWorldKartId() const { return m_world_kart_id; }
void setWorldKartId(unsigned int n) { m_world_kart_id=n; }
void loadData(RaceManager::KartType type, bool is_first_kart, Track* track, void loadData(RaceManager::KartType type, bool is_first_kart, Track* track,
bool animatedModel); bool animatedModel);
virtual void updateGraphics(float dt, const Vec3& off_xyz, virtual void updateGraphics(float dt, const Vec3& off_xyz,
@ -248,7 +242,8 @@ public:
void handleZipper (const Material *m=NULL, bool play_sound=false); void handleZipper (const Material *m=NULL, bool play_sound=false);
void setSquash (float time, float slowdown); void setSquash (float time, float slowdown);
void crashed (Kart *k, const Material *m=NULL); void crashed (Kart *k, bool update_attachments);
void crashed (const Material *m);
virtual void update (float dt); virtual void update (float dt);
virtual void finishedRace (float time); virtual void finishedRace (float time);
@ -257,6 +252,9 @@ public:
bool playCustomSFX (unsigned int type); bool playCustomSFX (unsigned int type);
void setController(Controller *controller); void setController(Controller *controller);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the index of this kart in world. */
unsigned int getWorldKartId() const { return m_world_kart_id; }
// ------------------------------------------------------------------------
/** Returns this kart's kart model. */ /** Returns this kart's kart model. */
KartModel* getKartModel() { return m_kart_model; } KartModel* getKartModel() { return m_kart_model; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -22,45 +22,45 @@
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "graphics/particle_emitter.hpp" #include "graphics/particle_emitter.hpp"
#include "graphics/particle_kind.hpp" #include "graphics/particle_kind.hpp"
#include "graphics/particle_kind_manager.hpp"
#include "karts/kart.hpp" #include "karts/kart.hpp"
#include "physics/btKart.hpp"
#include <iostream> #include <iostream>
KartGFX::KartGFX(const Kart *kart) : m_current_skid(KGFX_SKID1) KartGFX::KartGFX(const Kart *kart)
{ {
if(!UserConfigParams::m_graphical_effects) if(!UserConfigParams::m_graphical_effects)
{ {
for(unsigned int i=0; i<KGFX_COUNT; i++) for(unsigned int i=0; i<KGFX_COUNT; i++)
{
m_all_emitters.push_back(NULL); m_all_emitters.push_back(NULL);
m_all_particle_kinds.push_back(NULL);
}
return; return;
} }
m_kart = kart; m_kart = kart;
Vec3 position(0, kart->getKartHeight()*0.35f, Vec3 rear_center(0, kart->getKartHeight()*0.35f,
-kart->getKartLength()*0.35f); -kart->getKartLength()*0.35f);
// Create all effects. Note that they must be created // Create all effects. Note that they must be created
// in the order of KartGFXType. // in the order of KartGFXType.
addEffect(KGFX_NITRO, "nitro.xml", position); addEffect(KGFX_NITRO, "nitro.xml", rear_center);
addEffect(KGFX_ZIPPER, "zipper_fire.xml", position); addEffect(KGFX_ZIPPER, "zipper_fire.xml", rear_center);
addEffect(KGFX_SKID1, "skid1.xml", position); addEffect(KGFX_TERRAIN, "smoke.xml", Vec3(0,0,0));
addEffect(KGFX_SKID2, "skid2.xml", position); addEffect(KGFX_SKID1, "skid1.xml", rear_center);
addEffect(KGFX_SKID2, "skid2.xml", rear_center);
} // KartGFX } // KartGFX
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Destructor. Frees all particle effects and kinds.
*/
KartGFX::~KartGFX() KartGFX::~KartGFX()
{ {
for(unsigned int i=0; i<KGFX_COUNT; i++) for(unsigned int i=0; i<KGFX_COUNT; i++)
{ {
if(m_all_emitters[i]) if(m_all_emitters[i])
delete m_all_emitters[i]; delete m_all_emitters[i];
if(m_all_particle_kinds[i])
delete m_all_particle_kinds[i];
} // for i < KGFX_COUNT } // for i < KGFX_COUNT
} // ~KartGFX } // ~KartGFX
@ -75,17 +75,23 @@ KartGFX::~KartGFX()
void KartGFX::addEffect(KartGFXType type, const std::string &file_name, void KartGFX::addEffect(KartGFXType type, const std::string &file_name,
const Vec3 &position) const Vec3 &position)
{ {
ParticleKind *kind = NULL; const ParticleKind *kind = NULL;
ParticleEmitter *emitter = NULL; ParticleEmitter *emitter = NULL;
try try
{ {
kind = new ParticleKind(file_manager->getGfxFile(file_name));
if(type==KGFX_SKID2) kind = ParticleKindManager::get()->getParticles(file_name);
emitter = NULL; // skid2 is only used to store the emitter type //kind = new ParticleKind(file_manager->getGfxFile(file_name));
// Skid2 is only used to store the emitter type, and a wheeless
// kart has no terrain effects.
if(type==KGFX_SKID2 || (type==KGFX_TERRAIN && m_kart->isWheeless()) )
emitter = NULL;
else if(type==KGFX_TERRAIN)
// Terrain is NOT a child of the kart, since bullet returns the
// raycast info in world coordinates
emitter = new ParticleEmitter(kind, position);
else else
emitter = new ParticleEmitter(kind, emitter = new ParticleEmitter(kind, position, m_kart->getNode());
position,
m_kart->getNode());
} }
catch (std::runtime_error& e) catch (std::runtime_error& e)
{ {
@ -97,15 +103,20 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name,
kind = NULL; kind = NULL;
emitter = NULL; emitter = NULL;
} }
assert(m_all_emitters.size()==type); assert((int)m_all_emitters.size()==type);
m_all_emitters.push_back(emitter); m_all_emitters.push_back(emitter);
assert(m_all_particle_kinds.size()==type); if(type==KGFX_SKID1)
m_all_particle_kinds.push_back(kind); m_skid_kind1 = kind;
else if (type==KGFX_SKID2)
m_skid_kind2 = kind;
} // addEffect } // addEffect
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Resets all particle emitters. Used at the (re)start of a race.
*/
void KartGFX::reset() void KartGFX::reset()
{ {
m_wheel_toggle = 1;
for(unsigned int i=0; i<m_all_emitters.size(); i++) for(unsigned int i=0; i<m_all_emitters.size(); i++)
{ {
if(m_all_emitters[i]) if(m_all_emitters[i])
@ -121,44 +132,40 @@ void KartGFX::reset()
* \param type Must be either KGFX_SKID1 or KGFX_SKID2 - the particle type * \param type Must be either KGFX_SKID1 or KGFX_SKID2 - the particle type
* to use corresponding to the bonus level. * to use corresponding to the bonus level.
*/ */
void KartGFX::setSkidLevel(unsigned int level) void KartGFX::setSkidLevel(const unsigned int level)
{ {
KartGFXType type = level==1 ? KGFX_SKID1 : KGFX_SKID2; assert(level >= 1);
assert(type == KGFX_SKID1 || type==KGFX_SKID2); assert(level <= 2);
m_current_skid = type; const ParticleKind *pk = level==1 ? m_skid_kind1 : m_skid_kind2;
if(m_all_emitters[KGFX_SKID1]) if(m_all_emitters[KGFX_SKID1])
m_all_emitters[KGFX_SKID1]->setParticleType( m_all_emitters[KGFX_SKID1]->setParticleType(pk);
m_all_particle_kinds[type]);
} // setSkidLevel } // setSkidLevel
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Updates all gfx. /** Sets a new particle type to be used. Note that the memory of this
* \param dt Time step size. * kind must be managed by the caller.
* \param type The emitter type for which to set the new particle type.
* \param pk The particle kind to use.
*/ */
void KartGFX::update(float dt) void KartGFX::setParticleKind(const KartGFXType type, const ParticleKind *pk)
{ {
if(!UserConfigParams::m_graphical_effects) return; ParticleEmitter *pe = m_all_emitters[KGFX_TERRAIN];
if(!pe) return;
for(unsigned int i=0; i<m_all_emitters.size(); i++) pe->setParticleType(pk);
{ } // setParticleKind
if(m_all_emitters[i])
m_all_emitters[i]->update(dt);
}
} // update
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Sets the creation rate for the specified particle type relative to the /** Defines the new position of the specified emitter.
* given minimum and maximum particle rate. * \param type The emitter to set a new position for.
* \param type The particle effect for which to set the * \param xyz The new position of the emitter.
* creation rate.
* \param f The new relative creation rate.
*/ */
void KartGFX::setCreationRateRelative(KartGFXType type, float f) void KartGFX::setXYZ(const KartGFXType type, const Vec3 &xyz)
{ {
if(m_all_emitters[type]) ParticleEmitter *pe = m_all_emitters[KGFX_TERRAIN];
m_all_emitters[type]->setCreationRateRelative(f); if(!pe) return;
} // setCreationRate pe->setPosition(xyz);
} // setXYZ
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Sets the absolute creation rate for the specified particle type. /** Sets the absolute creation rate for the specified particle type.
@ -170,7 +177,26 @@ void KartGFX::setCreationRateAbsolute(KartGFXType type, float f)
{ {
if(m_all_emitters[type]) if(m_all_emitters[type])
m_all_emitters[type]->setCreationRateAbsolute(f); m_all_emitters[type]->setCreationRateAbsolute(f);
} // setCreationRate } // setCreationRateAbsolute
// ----------------------------------------------------------------------------
/** Sets the creation rate for the specified particle type relative to the
* given minimum and maximum particle rate. If a negative value is
* specified, the creation rate will be set to 0 (absolute).
* \param type The particle effect for which to set the
* creation rate (<0 means no more particles).
* \param f The new relative creation rate.
*/
void KartGFX::setCreationRateRelative(KartGFXType type, float f)
{
if(m_all_emitters[type])
{
if(f<0)
m_all_emitters[type]->setCreationRateAbsolute(0);
else
m_all_emitters[type]->setCreationRateRelative(f);
}
} // setCreationRateRelative
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Resize the area from which the particles are emitted: the emitter box /** Resize the area from which the particles are emitted: the emitter box
@ -185,4 +211,63 @@ void KartGFX::resizeBox(KartGFXType type, float speed, float dt)
{ {
if(m_all_emitters[type]) if(m_all_emitters[type])
m_all_emitters[type]->resizeBox(std::max(0.25f, speed*dt)); m_all_emitters[type]->resizeBox(std::max(0.25f, speed*dt));
} // resizeBox } // resizeBox
// ----------------------------------------------------------------------------
/** If necessary defines a new particle type for the terrain emitter. Then
* adjusts the location of the terrain emitter to be in synch with the
* current wheel position, and defines the emission rate depending on speed,
* steering, and skidding.
* \param pk Particle type to use.
*/
void KartGFX::updateTerrain(const ParticleKind *pk)
{
ParticleEmitter *pe = m_all_emitters[KGFX_TERRAIN];
if(!pe) return;
pe->setParticleType(pk);
const btWheelInfo &wi = m_kart->getVehicle()
->getWheelInfo(2+m_wheel_toggle);
Vec3 xyz = wi.m_raycastInfo.m_contactPointWS;
// FIXME: the X and Z position is not always accurate.
xyz.setX(xyz.getX()+ 0.06f * (m_wheel_toggle ? +1 : -1));
xyz.setZ(xyz.getZ()+0.06f);
pe->setPosition(xyz);
// Now compute the particle creation rate:
float rate = 0;
const float speed = fabsf(m_kart->getSpeed());
const float skidding = m_kart->getSkidding();
if (skidding > 1.0f)
rate = fabsf(m_kart->getControls().m_steer) > 0.8 ? skidding - 1 : 0;
else if (speed >= 0.5f)
rate = speed/m_kart->getKartProperties()->getMaxSpeed();
else
{
pe->setCreationRateAbsolute(0);
return;
}
// m_skidding can be > 2, and speed > maxSpeed (if powerups are used).
if(rate>1.0f) rate = 1.0f;
pe->setCreationRateRelative(rate);
} // updateTerrain
// ----------------------------------------------------------------------------
/** Updates all gfx.
* \param dt Time step size.
*/
void KartGFX::update(float dt)
{
if(!UserConfigParams::m_graphical_effects) return;
m_wheel_toggle = 1 - m_wheel_toggle;
for(unsigned int i=0; i<m_all_emitters.size(); i++)
{
if(m_all_emitters[i])
m_all_emitters[i]->update(dt);
}
} // update

View File

@ -34,18 +34,25 @@ class KartGFX
{ {
public: public:
/** All particle effects supported by this object. /** All particle effects supported by this object.
* Nitro, zipper, and skidding effects. KGFX_COUNT * Nitro, zipper, terrain, and skidding effects. Two different
* skid types are supported, but only one emitter node will be
* created. So KGFX_SKID1/2 store the two types, and KGFX_SKID
* = KGFX_SKID1 stores the actual emitter node. KGFX_COUNT
* is the number of entries and must therefore be last. */ * is the number of entries and must therefore be last. */
enum KartGFXType { KGFX_NITRO=0, enum KartGFXType { KGFX_NITRO=0,
KGFX_ZIPPER, KGFX_ZIPPER,
KGFX_TERRAIN,
KGFX_SKID, KGFX_SKID,
KGFX_SKID1=KGFX_SKID, KGFX_SKID1=KGFX_SKID,
KGFX_SKID2, KGFX_SKID2,
KGFX_COUNT}; KGFX_COUNT};
private: private:
/** Vector of all particle kinde. */ /** The particle kind for skidding bonus level 1. */
std::vector<ParticleKind*> m_all_particle_kinds; const ParticleKind *m_skid_kind1;
/** The particle kind for skidding bonus level 2. */
const ParticleKind *m_skid_kind2;
/** Vector of all particle emitters. */ /** Vector of all particle emitters. */
std::vector<ParticleEmitter*> m_all_emitters; std::vector<ParticleEmitter*> m_all_emitters;
@ -53,8 +60,8 @@ private:
/** Pointer to the owner of this kart. */ /** Pointer to the owner of this kart. */
const Kart *m_kart; const Kart *m_kart;
/** Indicates the current skidding level, either skid1 or skid2. */ /** Used to alternate particle effects from the rear wheels. */
KartGFXType m_current_skid; int m_wheel_toggle;
void addEffect(KartGFXType type, const std::string &file_name, void addEffect(KartGFXType type, const std::string &file_name,
const Vec3 &position); const Vec3 &position);
@ -63,10 +70,13 @@ public:
KartGFX(const Kart *kart); KartGFX(const Kart *kart);
~KartGFX(); ~KartGFX();
void reset(); void reset();
void setSkidLevel(const unsigned int level);
void setParticleKind(const KartGFXType type, const ParticleKind *pk);
void setXYZ(const KartGFXType type, const Vec3 &xyz);
void setCreationRateAbsolute(const KartGFXType type, float f);
void setCreationRateRelative(const KartGFXType type, float f);
void resizeBox(const KartGFXType type, float speed, float dt);
void updateTerrain(const ParticleKind *pk);
void update(float dt); void update(float dt);
void setCreationRateAbsolute(KartGFXType type, float f);
void setCreationRateRelative(KartGFXType type, float f);
void resizeBox(KartGFXType type, float speed, float dt);
void setSkidLevel(unsigned int level);
}; // KartWGFX }; // KartWGFX
#endif #endif

View File

@ -20,11 +20,12 @@
#include "items/item.hpp" #include "items/item.hpp"
KartWithStats::KartWithStats(const std::string& ident, Track* track, KartWithStats::KartWithStats(const std::string& ident,
unsigned int world_kart_id, Track* track,
int position, bool is_first_kart, int position, bool is_first_kart,
const btTransform& init_transform, const btTransform& init_transform,
RaceManager::KartType type) RaceManager::KartType type)
: Kart(ident, track, position, is_first_kart, : Kart(ident, world_kart_id, track, position, is_first_kart,
init_transform, type) init_transform, type)
{ {
reset(); reset();

View File

@ -65,7 +65,8 @@ private:
float m_skidding_time; float m_skidding_time;
public: public:
KartWithStats(const std::string& ident, Track* track, KartWithStats(const std::string& ident,
unsigned int world_kart_id, Track* track,
int position, bool is_first_kart, int position, bool is_first_kart,
const btTransform& init_transform, const btTransform& init_transform,
RaceManager::KartType type); RaceManager::KartType type);

View File

@ -874,7 +874,6 @@ int handleCmdLine(int argc, char **argv)
|| !strcmp(argv[i], "-N") ) || !strcmp(argv[i], "-N") )
{ {
UserConfigParams::m_no_start_screen = true; UserConfigParams::m_no_start_screen = true;
unlock_manager->setCurrentSlot(UserConfigParams::m_all_players[0].getName());
} }
else if ( !strcmp(argv[i], "--race-now") else if ( !strcmp(argv[i], "--race-now")
|| !strcmp(argv[i], "-R") ) || !strcmp(argv[i], "-R") )
@ -965,6 +964,9 @@ int handleCmdLine(int argc, char **argv)
return 0; return 0;
} }
} // for i <argc } // for i <argc
if(UserConfigParams::m_no_start_screen)
unlock_manager->setCurrentSlot(UserConfigParams::m_all_players[0]
.getName() );
if(ProfileWorld::isProfileMode()) if(ProfileWorld::isProfileMode())
{ {
UserConfigParams::m_sfx = false; // Disable sound effects UserConfigParams::m_sfx = false; // Disable sound effects

View File

@ -20,15 +20,57 @@
#include "input/input.hpp" #include "input/input.hpp"
#include "input/input_manager.hpp" #include "input/input_manager.hpp"
#include "karts/kart.hpp" #include "karts/kart.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/overworld.hpp" #include "modes/overworld.hpp"
#include "network/network_manager.hpp" #include "network/network_manager.hpp"
#include "states_screens/dialogs/select_challenge.hpp" #include "states_screens/dialogs/select_challenge.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/race_gui_overworld.hpp" #include "states_screens/race_gui_overworld.hpp"
#include "tracks/track.hpp" #include "tracks/track.hpp"
//-----------------------------------------------------------------------------
/** Function to simplify the start process */
void OverWorld::enterOverWorld()
{
race_manager->setNumLocalPlayers(1);
race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE);
race_manager->setMinorMode (RaceManager::MINOR_MODE_OVERWORLD);
race_manager->setNumKarts( 1 );
race_manager->setTrack( "overworld" );
race_manager->setDifficulty(RaceManager::RD_HARD);
// Use keyboard 0 by default (FIXME: let player choose?)
InputDevice* device = input_manager->getDeviceList()->getKeyboard(0);
// Create player and associate player with keyboard
StateManager::get()->createActivePlayer(
UserConfigParams::m_all_players.get(0), device );
if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL)
{
fprintf(stderr, "[MainMenuScreen] WARNING: cannot find kart '%s', will revert to default\n",
UserConfigParams::m_default_kart.c_str());
UserConfigParams::m_default_kart.revertToDefaults();
}
race_manager->setLocalKartInfo(0, UserConfigParams::m_default_kart);
// ASSIGN should make sure that only input from assigned devices
// is read.
input_manager->getDeviceList()->setAssignMode(ASSIGN);
input_manager->getDeviceList()
->setSinglePlayer( StateManager::get()->getActivePlayer(0) );
StateManager::get()->enterGameState();
network_manager->setupPlayerKartInfo();
race_manager->startNew();
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
OverWorld::OverWorld() : LinearWorld() OverWorld::OverWorld() : LinearWorld()
{ {
m_return_to_garage = false;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -56,11 +98,22 @@ void OverWorld::update(float dt)
const unsigned int kart_amount = m_karts.size(); const unsigned int kart_amount = m_karts.size();
// isn't cool, on the overworld nitro is free! // isn't it cool, on the overworld nitro is free!
for(unsigned int n=0; n<kart_amount; n++) for(unsigned int n=0; n<kart_amount; n++)
{ {
m_karts[n]->setEnergy(100.0f); m_karts[n]->setEnergy(100.0f);
} }
if (m_return_to_garage)
{
m_return_to_garage = false;
delayedSelfDestruct();
race_manager->exitRace(false);
KartSelectionScreen* s = KartSelectionScreen::getInstance();
s->setMultiplayer(false);
s->setFromOverworld(true);
StateManager::get()->resetAndGoToScreen(s);
}
} // update } // update
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -38,6 +38,8 @@ protected:
/** Override from base class */ /** Override from base class */
virtual void createRaceGUI(); virtual void createRaceGUI();
bool m_return_to_garage;
public: public:
OverWorld(); OverWorld();
/** call just after instanciating. can't be moved to the contructor as child /** call just after instanciating. can't be moved to the contructor as child
@ -46,6 +48,8 @@ public:
virtual void init(); virtual void init();
virtual ~OverWorld(); virtual ~OverWorld();
static void enterOverWorld();
virtual void update(float delta); virtual void update(float delta);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -69,6 +73,8 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Override settings from base class */ /** Override settings from base class */
virtual bool useChecklineRequirements() const { return false; } virtual bool useChecklineRequirements() const { return false; }
// ------------------------------------------------------------------------
void scheduleReturnToGarage() { m_return_to_garage = true; }
}; };
#endif #endif

View File

@ -91,8 +91,12 @@ Kart *ProfileWorld::createKart(const std::string &kart_ident, int index,
{ {
btTransform init_pos = m_track->getStartTransform(index); btTransform init_pos = m_track->getStartTransform(index);
Kart *new_kart = new KartWithStats(kart_ident, m_track, index+1, Kart *new_kart = new KartWithStats(kart_ident,
false, init_pos, /*world kart id*/ index,
m_track,
/*position*/ index+1,
/*is_first_kart*/false,
init_pos,
RaceManager::KT_AI); RaceManager::KT_AI);
Controller *controller = loadAIController(new_kart); Controller *controller = loadAIController(new_kart);

View File

@ -89,6 +89,7 @@ World::World() : WorldStatus(), m_clear_color(255,100,101,140)
m_clear_back_buffer = false; m_clear_back_buffer = false;
m_schedule_pause = false; m_schedule_pause = false;
m_schedule_unpause = false; m_schedule_unpause = false;
m_self_destruct = false;
WorldStatus::setClockMode(CLOCK_CHRONO); WorldStatus::setClockMode(CLOCK_CHRONO);
} // World } // World
@ -144,7 +145,6 @@ void World::init()
Kart* newkart = createKart(kart_ident, i, local_player_id, Kart* newkart = createKart(kart_ident, i, local_player_id,
global_player_id); global_player_id);
m_karts.push_back(newkart); m_karts.push_back(newkart);
newkart->setWorldKartId(m_karts.size()-1);
m_track->adjustForFog(newkart->getNode()); m_track->adjustForFog(newkart->getNode());
} // for i } // for i
@ -190,7 +190,7 @@ Kart *World::createKart(const std::string &kart_ident, int index,
{ {
int position = index+1; int position = index+1;
btTransform init_pos = m_track->getStartTransform(index); btTransform init_pos = m_track->getStartTransform(index);
Kart *new_kart = new Kart(kart_ident, m_track, position, Kart *new_kart = new Kart(kart_ident, index,m_track, position,
(local_player_id == 0), init_pos, (local_player_id == 0), init_pos,
race_manager->getKartType(index)); race_manager->getKartType(index));
Controller *controller = NULL; Controller *controller = NULL;
@ -590,6 +590,12 @@ void World::updateWorld(float dt)
m_schedule_unpause = false; m_schedule_unpause = false;
} }
if (m_self_destruct)
{
delete this;
return;
}
// Don't update world if a menu is shown or the race is over. // Don't update world if a menu is shown or the race is over.
if( m_phase == FINISH_PHASE || if( m_phase == FINISH_PHASE ||
m_phase == IN_GAME_MENU_PHASE ) m_phase == IN_GAME_MENU_PHASE )
@ -600,7 +606,6 @@ void World::updateWorld(float dt)
{ {
enterRaceOverState(); enterRaceOverState();
} }
} // updateWorld } // updateWorld
#define MEASURE_FPS 0 #define MEASURE_FPS 0
@ -948,4 +953,13 @@ void World::unpause()
} }
} // pause } // pause
//-----------------------------------------------------------------------------
/** Call when the world needs to be deleted but you can't do it immediately
* because you are e.g. within World::update()
*/
void World::delayedSelfDestruct()
{
m_self_destruct = true;
}
/* EOF */ /* EOF */

View File

@ -146,6 +146,11 @@ protected:
Phase m_scheduled_pause_phase; Phase m_scheduled_pause_phase;
/** Set when the world needs to be deleted but you can't do it immediately
* because you are e.g. within World::update()
*/
bool m_self_destruct;
public: public:
World(); World();
virtual ~World(); virtual ~World();
@ -276,10 +281,12 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Override if you want to know when a kart presses fire */ /** Override if you want to know when a kart presses fire */
virtual void onFirePressed(Controller* who) {} virtual void onFirePressed(Controller* who) {}
// ------------------------------------------------------------------------
/** Whether to compute checkline requirements for each world on the /** Whether to compute checkline requirements for each world on the
* quadgraph. Override to change value. */ * quadgraph. Override to change value. */
virtual bool useChecklineRequirements() const { return false; } virtual bool useChecklineRequirements() const { return false; }
// ------------------------------------------------------------------------
void delayedSelfDestruct();
}; // World }; // World

View File

@ -23,10 +23,13 @@
/** A network kart. On the server, it receives its control information (steering etc) /** A network kart. On the server, it receives its control information (steering etc)
from the network manager. from the network manager.
*/ */
NetworkKart::NetworkKart(const std::string &kart_name, Track* track, int position, NetworkKart::NetworkKart(const std::string &kart_name,
unsigned int world_kart_id, Track* track,
int position,
const btTransform &init_transform, int global_player_id, const btTransform &init_transform, int global_player_id,
RaceManager::KartType type) RaceManager::KartType type)
: Kart(kart_name, track, position, false, init_transform, type) : Kart(kart_name, world_kart_id, track, position,
/*is_first_kart*/false, init_transform, type)
{ {
m_global_player_id = global_player_id; m_global_player_id = global_player_id;
} // NetworkKart } // NetworkKart

View File

@ -28,7 +28,8 @@ class NetworkKart : public Kart
private: private:
int m_global_player_id; // to identify this kart to the network manager int m_global_player_id; // to identify this kart to the network manager
public: public:
NetworkKart(const std::string& kart_name, Track* track, int position, NetworkKart(const std::string& kart_name, unsigned int world_kart_id,
Track* track, int position,
const btTransform& init_transform, const btTransform& init_transform,
int global_player_id, RaceManager::KartType type); int global_player_id, RaceManager::KartType type);
void setControl(const KartControl& kc); void setControl(const KartControl& kc);

View File

@ -438,8 +438,11 @@ void btKart::updateVehicle( btScalar step )
if(m_time_additional_impulse>0) if(m_time_additional_impulse>0)
{ {
m_time_additional_impulse -= step; float dt = step > m_time_additional_impulse
m_chassisBody->applyCentralImpulse(m_additional_impulse); ? m_time_additional_impulse
: step;
m_chassisBody->applyCentralImpulse(m_additional_impulse*dt);
m_time_additional_impulse -= dt;
} }
if(m_time_additional_rotation>0) if(m_time_additional_rotation>0)
@ -461,7 +464,7 @@ void btKart::updateVehicle( btScalar step )
// kart, or a strongly 'visual jolt' of the kart // kart, or a strongly 'visual jolt' of the kart
btTransform &iwt=m_chassisBody->getInterpolationWorldTransform(); btTransform &iwt=m_chassisBody->getInterpolationWorldTransform();
iwt.setRotation(iwt.getRotation()*add_rot); iwt.setRotation(iwt.getRotation()*add_rot);
m_time_additional_rotation -= step; m_time_additional_rotation -= dt;
} }
} // updateVehicle } // updateVehicle
@ -724,30 +727,33 @@ void btKart::updateFriction(btScalar timeStep)
rollingFriction=0; rollingFriction=0;
} }
//switch between active rolling (throttle), braking and non-active
// rolling friction (no throttle/break)
m_wheelInfo[wheel].m_skidInfo= btScalar(1.);
btScalar maximp = wheelInfo.m_wheelsSuspensionForce
* timeStep * wheelInfo.m_frictionSlip;
btScalar maximpSide = maximp;
btScalar maximpSquared = maximp * maximpSide;
m_forwardImpulse[wheel] = rollingFriction; m_forwardImpulse[wheel] = rollingFriction;
if(m_time_additional_impulse>0)
btScalar x = (m_forwardImpulse[wheel] ) * fwdFactor;
btScalar y = (m_sideImpulse[wheel] ) * sideFactor;
btScalar impulseSquared = (x*x + y*y);
if (impulseSquared > maximpSquared)
{ {
sliding = true; sliding = true;
btScalar factor = maximp / btSqrt(impulseSquared); m_wheelInfo[wheel].m_skidInfo = 0.0f;
m_wheelInfo[wheel].m_skidInfo *= factor; }
} // if impulseSquared > maximpSquared else
{
btScalar maximp = wheelInfo.m_wheelsSuspensionForce
* timeStep * wheelInfo.m_frictionSlip;
btScalar maximpSide = maximp;
btScalar maximpSquared = maximp * maximpSide;
btScalar x = (m_forwardImpulse[wheel] ) * fwdFactor;
btScalar y = (m_sideImpulse[wheel] ) * sideFactor;
btScalar impulseSquared = (x*x + y*y);
if (impulseSquared > maximpSquared)
{
sliding = true;
btScalar factor = maximp / btSqrt(impulseSquared);
m_wheelInfo[wheel].m_skidInfo *= factor;
} // if impulseSquared > maximpSquared
} // else (!m_timed_impulse
} // for (int wheel=0; wheel<getNumWheels(); wheel++)
} // for (int wheel=0; wheel<getNumWheels(); wheel++)
m_zipper_active = false; m_zipper_active = false;
m_zipper_velocity = 0; m_zipper_velocity = 0;
@ -779,7 +785,7 @@ void btKart::updateFriction(btScalar timeStep)
av.setY(m_skid_angular_velocity); av.setY(m_skid_angular_velocity);
m_chassisBody->setAngularVelocity(av); m_chassisBody->setAngularVelocity(av);
} }
else if (sliding && m_allow_sliding) else if (sliding && (m_allow_sliding || m_time_additional_impulse>0) )
{ {
for (int wheel = 0; wheel < getNumWheels(); wheel++) for (int wheel = 0; wheel < getNumWheels(); wheel++)
{ {

View File

@ -230,7 +230,7 @@ public:
} // setTimedImpulse } // setTimedImpulse
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the time an additional impulse is activated. */ /** Returns the time an additional impulse is activated. */
float getImpulseTime() const { return m_time_additional_impulse; } float getCentralImpulseTime() const { return m_time_additional_impulse; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets a rotation that is applied over a certain amount of time (to avoid /** Sets a rotation that is applied over a certain amount of time (to avoid
* a too rapid changes in the kart). * a too rapid changes in the kart).

View File

@ -221,6 +221,11 @@ bool Physics::projectKartDownwards(const Kart *k)
Physics::CollisionSide Physics::getCollisionSide(const btRigidBody *body, Physics::CollisionSide Physics::getCollisionSide(const btRigidBody *body,
const Vec3 &contact_point) const Vec3 &contact_point)
{ {
if(contact_point.getX()>0)
return COL_RIGHT;
else
return COL_LEFT;
btVector3 aabb_min, aabb_max; btVector3 aabb_min, aabb_max;
static btTransform zero_trans(btQuaternion(0, 0, 0)); static btTransform zero_trans(btQuaternion(0, 0, 0));
body->getCollisionShape()->getAabb(zero_trans, aabb_min, aabb_max); body->getCollisionShape()->getAabb(zero_trans, aabb_min, aabb_max);
@ -268,135 +273,118 @@ Physics::CollisionSide Physics::getCollisionSide(const btRigidBody *body,
* server and if no networking is used, and from race_state on the client to * server and if no networking is used, and from race_state on the client to
* replay what happened on the server. * replay what happened on the server.
* \param kart_a First kart involved in the collision. * \param kart_a First kart involved in the collision.
* \param contact_point_a Location of collision at first kart (in kart
* coordinates).
* \param kart_b Second kart involved in the collision. * \param kart_b Second kart involved in the collision.
* \param contact_point_b Location of collision at second kart (in kart
* coordinates).
*/ */
void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a, void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a,
Kart *kart_b, const Vec3 &contact_point_b) Kart *kart_b, const Vec3 &contact_point_b)
{ {
kart_a->crashed(kart_b); // will play crash sound for player karts // Only one kart needs to handle the attachments, it will
kart_b->crashed(kart_a); // fix the attachments for the other kart.
Attachment *attachmentA=kart_a->getAttachment(); kart_a->crashed(kart_b, /*handle_attachments*/true);
Attachment *attachmentB=kart_b->getAttachment(); kart_b->crashed(kart_a, /*handle_attachments*/false);
if(attachmentA->getType()==Attachment::ATTACH_BOMB) Kart *left_kart, *right_kart;
// Determine which kart is pushed to the left, and which one to the
// right. Ideally the sign of the X coordinate of the local conact point
// could decide the direction (negative X --> was hit on left side, gets
// push to right), but that can lead to both karts being pushed in the
// same direction (front left of kart hits rear left).
// So we just use a simple test (which does the right thing in ideal
// crashes, but avoids pushing both karts in corner cases
// - pun intended ;) ).
if(contact_point_a.getX() < contact_point_b.getX())
{ {
// If both karts have a bomb, explode them immediately: left_kart = kart_a;
if(attachmentB->getType()==Attachment::ATTACH_BOMB) right_kart = kart_b;
{
attachmentA->setTimeLeft(0.0f);
attachmentB->setTimeLeft(0.0f);
}
else // only A has a bomb, move it to B (unless it was from B)
{
if(attachmentA->getPreviousOwner()!=kart_b)
{
attachmentA->moveBombFromTo(kart_a, kart_b);
// Play appropriate SFX
kart_b->playCustomSFX(SFXManager::CUSTOM_ATTACH);
}
}
}
else if(attachmentB->getType()==Attachment::ATTACH_BOMB &&
attachmentB->getPreviousOwner()!=kart_a)
{
attachmentB->moveBombFromTo(kart_b, kart_a);
kart_a->playCustomSFX(SFXManager::CUSTOM_ATTACH);
} }
else else
{ {
kart_a->playCustomSFX(SFXManager::CUSTOM_CRASH); left_kart = kart_b;
kart_b->playCustomSFX(SFXManager::CUSTOM_CRASH); right_kart = kart_a;
} }
// If bouncing crashes is enabled, add an additional force to the // Add a scaling factor depending on the mass (avoid div by zero)
// slower kart float f_right = right_kart->getKartProperties()->getMass() > 0
Kart *faster_kart, *slower_kart; ? left_kart->getKartProperties()->getMass()
Vec3 faster_cp, slower_cp; / right_kart->getKartProperties()->getMass()
if(kart_a->getSpeed()>=kart_b->getSpeed()) : 1.5f;
// Add a scaling factor depending on speed (avoid div by 0)
f_right *= right_kart->getSpeed() > 0
? left_kart->getSpeed()
/ right_kart->getSpeed()
: 1.5f;
// Cap f_right to [0.8,1.25], which results in f_left being
// capped in the same interval
if(f_right > 1.25f)
f_right = 1.25f;
else if(f_right< 0.8f)
f_right = 0.8f;
float f_left = f_right ==0 ? 1.5f : 1/f_right;
// Check if a kart is more 'actively' trying to push another kart
// by checking its local sidewards velocity
float vel_left = left_kart->getVelocityLC().getX();
float vel_right = right_kart->getVelocityLC().getX();
// Use the difference in speed to determine which kart gets a
// ramming bonus. Normally vel_right and vel_left will have
// a different sign: right kart will be driving to the left,
// and left kart to the right (both pushing at each other).
// By using the sum we get the intended effect: if both karts
// are pushing with the same speed, vel_diff is 0, if the right
// kart is driving faster vel_diff will be < 0. If both velocities
// have the same sign, one kart is trying to steer away from the
// other, in which case it gets an even bigger push.
float vel_diff = vel_right + vel_left;
// More driving towards left --> left kart gets bigger impulse
if(vel_diff<0)
{ {
faster_kart = kart_a; f_right *= vel_left == 0 ? 2.0f : (1.0f - vel_diff/fabsf(vel_left));
faster_cp = contact_point_a; if(f_right > 2.0f)
slower_kart = kart_b; f_right = 2.0f;
slower_cp = contact_point_b;
} }
else else
{ {
faster_kart = kart_b; f_left *= vel_right == 0 ? 2.0f : (1.0f + vel_diff/fabsf(vel_right));
faster_cp = contact_point_b; if(f_left > 2.0f)
slower_kart = kart_a; f_left = 2.0f;
slower_cp = contact_point_a;
} }
CollisionSide faster_side = getCollisionSide(faster_kart->getBody(), // Increase the effect somewhat by squaring the factors
faster_cp); f_left = f_left * f_left;
CollisionSide slower_side = getCollisionSide(slower_kart->getBody(), f_right = f_right * f_right;
slower_cp);
// This probably needs adjusting once we have different kart properties. // First push one kart to the left (if there is not already
// E.g. besides speed we might also want to take mass into account(?) // an impulse happening - one collision might cause more
if(faster_side==COL_FRONT) // than one impulse otherwise)
if(right_kart->getVehicle()->getCentralImpulseTime()<=0)
{ {
// Special case: the faster kart hits a kart front on. In this case const KartProperties *kp = left_kart->getKartProperties();
// the slower kart will be pushed out of the faster kart's way Vec3 impulse(-kp->getCollisionImpulse()*f_left, 0, 0);
Vec3 dir = faster_kart->getVelocity(); impulse = right_kart->getTrans().getBasis() * impulse;
right_kart->getVehicle()
// The direction in which the impulse will be applied depends on ->setTimedCentralImpulse(kp->getCollisionImpulseTime(),
// which side of the faster kart was hitting it: if the hit is impulse);
// on the right side of the faster kart, it will push the slower right_kart ->getBody()->setAngularVelocity(btVector3(0,0,0));
// kart to the right and vice versa. This is based on the
// assumption that a hit to the right indicates that it's
// shorter to push the slower kart to the right.
Vec3 impulse;
if(faster_cp.getX()>0)
impulse = Vec3( dir.getZ(), 0, -dir.getX());
else
impulse = Vec3(-dir.getZ(), 0, dir.getX());
impulse.normalize();
impulse *= faster_kart->getKartProperties()->getCollisionImpulse();
float t =
faster_kart->getKartProperties()->getCollisionImpulseTime();
if(t>0)
slower_kart->getVehicle()->setTimedCentralImpulse(t, impulse);
else
slower_kart->getBody()->applyCentralImpulse(impulse);
slower_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
// Apply some impulse to the slower kart as well?
} }
else
// Then push the other kart to the right (if there is no
// impulse happening atm).
if(left_kart->getVehicle()->getCentralImpulseTime()<=0)
{ {
// Non-frontal collision, push the two karts away from each other const KartProperties *kp = right_kart->getKartProperties();
// First the faster kart Vec3 impulse = Vec3(kp->getCollisionImpulse()*f_right, 0, 0);
Vec3 dir = faster_kart->getVelocity(); impulse = left_kart->getTrans().getBasis() * impulse;
Vec3 impulse; left_kart->getVehicle()
if(faster_cp.getX()>0) ->setTimedCentralImpulse(kp->getCollisionImpulseTime(),
impulse = Vec3(-dir.getZ(), 0, dir.getX()); impulse);
else left_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
impulse = Vec3( dir.getZ(), 0, -dir.getX());
impulse.normalize();
impulse *= slower_kart->getKartProperties()->getCollisionImpulse();
float t =
faster_kart->getKartProperties()->getCollisionImpulseTime();
if(t>0)
faster_kart->getVehicle()->setTimedCentralImpulse(t, impulse);
else
faster_kart->getBody()->applyCentralImpulse(impulse);
faster_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
// Then the slower kart
dir = slower_kart->getVelocity();
if(slower_cp.getX()>0)
impulse = Vec3(-dir.getZ(), 0, dir.getX());
else
impulse = Vec3( dir.getZ(), 0, -dir.getX());
impulse.normalize();
impulse *= faster_kart->getKartProperties()->getCollisionImpulse();
t = faster_kart->getKartProperties()->getCollisionImpulseTime();
if(t>0)
slower_kart->getVehicle()->setTimedCentralImpulse(t, impulse);
else
slower_kart->getBody()->applyCentralImpulse(impulse);
slower_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
} }
} // KartKartCollision } // KartKartCollision
@ -470,7 +458,7 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies,
const Material *m const Material *m
= n>=0 ? upA->getPointerTriangleMesh()->getMaterial(n) = n>=0 ? upA->getPointerTriangleMesh()->getMaterial(n)
: NULL; : NULL;
kart->crashed(NULL, m); kart->crashed(m);
} }
} }
// 2) object a is a kart // 2) object a is a kart
@ -485,7 +473,7 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies,
const Material *m const Material *m
= n>=0 ? upB->getPointerTriangleMesh()->getMaterial(n) = n>=0 ? upB->getPointerTriangleMesh()->getMaterial(n)
: NULL; : NULL;
kart->crashed(NULL, m); // Kart hit track kart->crashed(m); // Kart hit track
} }
else if(upB->is(UserPointer::UP_FLYABLE)) else if(upB->is(UserPointer::UP_FLYABLE))
// 2.1 projectile hits kart // 2.1 projectile hits kart

View File

@ -138,7 +138,7 @@ private:
btCollisionDispatcher *m_dispatcher; btCollisionDispatcher *m_dispatcher;
btBroadphaseInterface *m_axis_sweep; btBroadphaseInterface *m_axis_sweep;
btDefaultCollisionConfiguration *m_collision_conf; btDefaultCollisionConfiguration *m_collision_conf;
CollisionList m_all_collisions; CollisionList m_all_collisions;
public: public:
Physics (); Physics ();

View File

@ -490,7 +490,7 @@ void RaceManager::computeGPRanks()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void RaceManager::exitRace() void RaceManager::exitRace(bool delete_world)
{ {
// Only display the grand prix result screen if all tracks // Only display the grand prix result screen if all tracks
// were finished, and not when a race is aborted. // were finished, and not when a race is aborted.
@ -559,7 +559,8 @@ void RaceManager::exitRace()
} }
} }
World::deleteWorld(); if (delete_world) World::deleteWorld();
m_track_number = 0; m_track_number = 0;
} // exitRace } // exitRace

View File

@ -483,7 +483,7 @@ public:
* \note In GP, displays the GP result screen first * \note In GP, displays the GP result screen first
* \note Deletes the world. * \note Deletes the world.
*/ */
void exitRace(); void exitRace(bool delete_world=true);
/** /**
* \brief Higher-level method to start a GP without having to care about the exact startup sequence * \brief Higher-level method to start a GP without having to care about the exact startup sequence

View File

@ -309,7 +309,7 @@ RaceOverDialog::RaceOverDialog(const float percentWidth,
} }
// ---- Buttons at the bottom // ---- Buttons at the bottom
if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0)
{ {
const int label_y = m_area.getHeight() - (button_h + margin_between_buttons)*2; const int label_y = m_area.getHeight() - (button_h + margin_between_buttons)*2;
@ -480,14 +480,14 @@ GUIEngine::EventPropagation RaceOverDialog::processEvent(const std::string& even
else if (eventSource == "seeunlocked") else if (eventSource == "seeunlocked")
{ {
std::vector<const ChallengeData*> unlocked = std::vector<const ChallengeData*> unlocked =
unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges();
unlock_manager->getCurrentSlot()->clearUnlocked(); unlock_manager->getCurrentSlot()->clearUnlocked();
FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance(); FeatureUnlockedCutScene::getInstance();
assert(unlocked.size() > 0); assert(unlocked.size() > 0);
scene->addUnlockedThings(unlocked); scene->addTrophy(race_manager->getDifficulty());
ModalDialog::dismiss(); ModalDialog::dismiss();
@ -505,7 +505,7 @@ GUIEngine::EventPropagation RaceOverDialog::processEvent(const std::string& even
void RaceOverDialog::escapePressed() void RaceOverDialog::escapePressed()
{ {
if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0)
{ {
std::string what = "seeunlocked"; std::string what = "seeunlocked";
processEvent(what); processEvent(what);

View File

@ -18,13 +18,47 @@
#include "challenges/unlock_manager.hpp" #include "challenges/unlock_manager.hpp"
#include "config/user_config.hpp" #include "config/user_config.hpp"
#include "guiengine/engine.hpp" #include "guiengine/engine.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "input/device_manager.hpp" #include "input/device_manager.hpp"
#include "input/input_manager.hpp" #include "input/input_manager.hpp"
#include "io/file_manager.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "network/network_manager.hpp" #include "network/network_manager.hpp"
#include "race/race_manager.hpp" #include "race/race_manager.hpp"
#include "states_screens/dialogs/select_challenge.hpp" #include "states_screens/dialogs/select_challenge.hpp"
using namespace GUIEngine;
// ----------------------------------------------------------------------------
core::stringw getLabel(RaceManager::Difficulty difficulty, const ChallengeData* c)
{
core::stringw label = _("Number of AI Karts : %i",
c->getNumKarts(difficulty) - 1);
if (c->getPosition(difficulty) != -1)
{
label.append(L"\n");
label.append( _("Required Rank : %i", c->getPosition(difficulty)) );
}
if (c->getTime(difficulty) > 0)
{
label.append(L"\n");
label.append( _("Required Time : %i",
StringUtils::timeToString(c->getTime(difficulty)).c_str()) );
}
if (c->getEnergy(difficulty) > 0)
{
label.append(L"\n");
label.append( _("Required Nitro Points : %i", c->getEnergy(difficulty)) );
}
return label;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
SelectChallengeDialog::SelectChallengeDialog(const float percentWidth, SelectChallengeDialog::SelectChallengeDialog(const float percentWidth,
const float percentHeight, const float percentHeight,
@ -35,14 +69,61 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth,
m_challenge_id = challenge_id; m_challenge_id = challenge_id;
World::getWorld()->schedulePause(WorldStatus::IN_GAME_MENU_PHASE); World::getWorld()->schedulePause(WorldStatus::IN_GAME_MENU_PHASE);
// TODO: select the previously selected difficulty switch (UserConfigParams::m_difficulty)
{
case 0:
getWidget("novice")->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
break;
case 1:
getWidget("intermediate")->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
break;
case 2:
getWidget("expert")->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
break;
}
const Challenge* c = unlock_manager->getCurrentSlot()->getChallenge(challenge_id);
if (c->isSolved(RaceManager::RD_EASY))
{
IconButtonWidget* btn = getWidget<IconButtonWidget>("novice");
btn->setImage(file_manager->getTextureFile("cup_bronze.png").c_str(),
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
}
if (c->isSolved(RaceManager::RD_MEDIUM))
{
IconButtonWidget* btn = getWidget<IconButtonWidget>("intermediate");
btn->setImage(file_manager->getTextureFile("cup_silver.png").c_str(),
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
}
if (c->isSolved(RaceManager::RD_HARD))
{
IconButtonWidget* btn = getWidget<IconButtonWidget>("expert");
btn->setImage(file_manager->getTextureFile("cup_gold.png").c_str(),
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
}
LabelWidget* novice_label = getWidget<LabelWidget>("novice_label");
LabelWidget* medium_label = getWidget<LabelWidget>("intermediate_label");
LabelWidget* expert_label = getWidget<LabelWidget>("difficult_label");
novice_label->setText( getLabel(RaceManager::RD_EASY, c->getData()), false );
medium_label->setText( getLabel(RaceManager::RD_MEDIUM, c->getData()), false );
expert_label->setText( getLabel(RaceManager::RD_HARD, c->getData()), false );
} }
// ----------------------------------------------------------------------------
SelectChallengeDialog::~SelectChallengeDialog() SelectChallengeDialog::~SelectChallengeDialog()
{ {
World::getWorld()->scheduleUnpause(); World::getWorld()->scheduleUnpause();
} }
// ----------------------------------------------------------------------------
GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::string& eventSourceParam) GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::string& eventSourceParam)
{ {
std::string eventSource = eventSourceParam; std::string eventSource = eventSourceParam;
@ -94,14 +175,17 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin
if (eventSource == "novice") if (eventSource == "novice")
{ {
challenge->setRace(RaceManager::RD_EASY); challenge->setRace(RaceManager::RD_EASY);
UserConfigParams::m_difficulty = 0;
} }
else if (eventSource == "intermediate") else if (eventSource == "intermediate")
{ {
challenge->setRace(RaceManager::RD_MEDIUM); challenge->setRace(RaceManager::RD_MEDIUM);
UserConfigParams::m_difficulty = 1;
} }
else if (eventSource == "expert") else if (eventSource == "expert")
{ {
challenge->setRace(RaceManager::RD_HARD); challenge->setRace(RaceManager::RD_HARD);
UserConfigParams::m_difficulty = 2;
} }
else else
{ {
@ -119,3 +203,5 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin
return GUIEngine::EVENT_LET; return GUIEngine::EVENT_LET;
} }
// ----------------------------------------------------------------------------

View File

@ -20,6 +20,8 @@
#include "guiengine/screen.hpp" #include "guiengine/screen.hpp"
#include "guiengine/widgets/button_widget.hpp" #include "guiengine/widgets/button_widget.hpp"
#include "guiengine/widgets/icon_button_widget.hpp" #include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "guiengine/widgets/spinner_widget.hpp"
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "karts/kart_properties_manager.hpp" #include "karts/kart_properties_manager.hpp"
#include "network/network_manager.hpp" #include "network/network_manager.hpp"
@ -49,52 +51,32 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin
const irr::core::stringw& trackName, ITexture* screenshot, const irr::core::stringw& trackName, ITexture* screenshot,
const float w, const float h) : ModalDialog(w, h) const float w, const float h) : ModalDialog(w, h)
{ {
loadFromFile("track_info_dialog.stkgui");
const bool has_laps = race_manager->modeHasLaps(); const bool has_laps = race_manager->modeHasLaps();
const bool has_highscores = race_manager->modeHasHighscores(); const bool has_highscores = race_manager->modeHasHighscores();
const int y1 = m_area.getHeight()/7;
const int y2 = m_area.getHeight()*5/7;
const int y3 = m_area.getHeight()*6/7;
m_track_ident = trackIdent; m_track_ident = trackIdent;
m_ribbon_item = ribbonItem; m_ribbon_item = ribbonItem;
// ---- Track title getWidget<LabelWidget>("name")->setText(trackName.c_str(), false);
core::rect< s32 > area_top(0, 0, m_area.getWidth(), y1);
IGUIStaticText* a = GUIEngine::getGUIEnv()->addStaticText( trackName.c_str(),
area_top, false, true, // border, word warp
m_irrlicht_window);
a->setTabStop(false);
const int hscores_y_from = y1;
const int hscores_y_to = y1 + (y2 - y1)*2/3;
// ---- Track credits
Track* track = track_manager->getTrack(trackIdent); Track* track = track_manager->getTrack(trackIdent);
core::rect< s32 > creator_info_area(0, hscores_y_to, m_area.getWidth()/2, y2);
//I18N: when showing who is the author of track '%s' (place %s where the name of the author should appear) //I18N: when showing who is the author of track '%s' (place %s where the name of the author should appear)
stringw text = _("Track by %s", track->getDesigner().c_str()); getWidget<LabelWidget>("author")->setText( _("Track by %s", track->getDesigner().c_str()), false );
IGUIStaticText* b = GUIEngine::getGUIEnv()->addStaticText( text.c_str(),
creator_info_area, false , true , // border, word warp
m_irrlicht_window);
b->setTabStop(false);
// ---- Track screenshot // ---- Track screenshot
Widget* screenshot_div = getWidget("screenshot_div");
IconButtonWidget* screenshotWidget = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO, IconButtonWidget* screenshotWidget = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
false /* tab stop */, false /* focusable */); false /* tab stop */, false /* focusable */);
// images are saved squared, but must be stretched to 4: // images are saved squared, but must be stretched to 4:
screenshotWidget->setCustomAspectRatio(4.0f / 3.0f); screenshotWidget->setCustomAspectRatio(4.0f / 3.0f);
core::rect< s32 > area_right(m_area.getWidth()/2, y1, m_area.getWidth(), y2-10);
screenshotWidget->m_x = area_right.UpperLeftCorner.X; screenshotWidget->m_x = screenshot_div->m_x;
screenshotWidget->m_y = area_right.UpperLeftCorner.Y; screenshotWidget->m_y = screenshot_div->m_y;
screenshotWidget->m_w = area_right.getWidth(); screenshotWidget->m_w = screenshot_div->m_w;
screenshotWidget->m_h = area_right.getHeight(); screenshotWidget->m_h = screenshot_div->m_h;
// temporary icon, will replace it just after (but it will be shown if the given icon is not found) // temporary icon, will replace it just after (but it will be shown if the given icon is not found)
screenshotWidget->m_properties[PROP_ICON] = "gui/main_help.png"; screenshotWidget->m_properties[PROP_ICON] = "gui/main_help.png";
@ -107,73 +89,39 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin
} }
m_widgets.push_back(screenshotWidget); m_widgets.push_back(screenshotWidget);
a->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
b->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
// ---- Lap count m_spinner // ---- Lap count m_spinner
if (has_laps) if (has_laps)
{ {
m_spinner = new SpinnerWidget(); m_spinner = getWidget<SpinnerWidget>("lapcountspinner");
m_spinner->m_x = m_area.getWidth()/2 - 200;
m_spinner->m_y = y2;
m_spinner->m_w = 400;
m_spinner->m_h = y3 - y2 - 15;
m_spinner->setParent(m_irrlicht_window);
m_spinner->m_properties[PROP_ID] = "lapcountspinner"; m_spinner->m_properties[PROP_ID] = "lapcountspinner";
if (UserConfigParams::m_artist_debug_mode) if (UserConfigParams::m_artist_debug_mode)
{ {
m_spinner->m_properties[PROP_MIN_VALUE] = "0"; m_spinner->setMin(0);
} }
else
{
m_spinner->m_properties[PROP_MIN_VALUE] = "1";
}
m_spinner->m_properties[PROP_MAX_VALUE] = "99";
m_spinner->m_properties[PROP_WARP_AROUND] = "true";
//I18N: In the track setup screen (number of laps choice, where %i is the number) //I18N: In the track setup screen (number of laps choice, where %i is the number)
m_spinner->setText( _("%i laps") ); //m_spinner->setText( _("%i laps") );
m_widgets.push_back(m_spinner);
m_spinner->add();
m_spinner->setValue( UserConfigParams::m_num_laps ); m_spinner->setValue( UserConfigParams::m_num_laps );
m_spinner->getIrrlichtElement()->setTabStop(true); //m_spinner->getIrrlichtElement()->setTabStop(true);
m_spinner->getIrrlichtElement()->setTabGroup(false); //m_spinner->getIrrlichtElement()->setTabGroup(false);
const int num_laps = m_spinner->getValue();
race_manager->setNumLaps(num_laps);
} }
else else
{ {
m_spinner = NULL; m_spinner = NULL;
} }
// Reverse track // Reverse track
const bool reverse_available = track->reverseAvailable(); const bool reverse_available = track->reverseAvailable();
if (reverse_available) if (reverse_available)
{ {
m_checkbox = new CheckBoxWidget(); m_checkbox = getWidget<CheckBoxWidget>("reverse");
m_checkbox->m_x = 0;
m_checkbox->m_y = (y2+y3)/2;
m_checkbox->m_w = 60;
m_checkbox->m_h = 60;
m_checkbox->setParent(m_irrlicht_window);
m_checkbox->m_properties[PROP_ID] = "reversecheckbox";
m_checkbox->m_properties[PROP_WARP_AROUND] = "true";
//~ m_checkbox->setText( _("Reverse track") );
m_widgets.push_back(m_checkbox);
m_checkbox->add();
m_checkbox->setState(race_manager->getReverseTrack()); m_checkbox->setState(race_manager->getReverseTrack());
m_checkbox->getIrrlichtElement()->setTabStop(true);
m_checkbox->getIrrlichtElement()->setTabGroup(false);
stringw text_reverse = _("Reverse");
IGUIStaticText* b = GUIEngine::getGUIEnv()->addStaticText(
text_reverse.c_str(),
core::rect< s32 >(m_checkbox->m_x+m_checkbox->m_w,
m_checkbox->m_y+10, m_checkbox->m_x+200,
m_checkbox->m_y+m_checkbox->m_h),
false , true , // border, word warp
m_irrlicht_window);
b->setTabStop(false);
} }
else else
{ {
@ -181,33 +129,35 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin
race_manager->setReverseTrack(false); race_manager->setReverseTrack(false);
} }
// ---- Start button
ButtonWidget* okBtn = new ButtonWidget();
okBtn->m_properties[PROP_ID] = "start";
okBtn->setText( _("Start Race") );
okBtn->m_x = m_area.getWidth()/2 - 200;
okBtn->m_y = y3;
okBtn->m_w = 400;
okBtn->m_h = m_area.getHeight() - y3 - 15;
okBtn->setParent(m_irrlicht_window);
m_widgets.push_back(okBtn);
okBtn->add();
okBtn->getIrrlichtElement()->setTabStop(true);
okBtn->getIrrlichtElement()->setTabGroup(false);
// ---- High Scores // ---- High Scores
if (has_highscores) if (has_highscores)
{ {
addHighScoreWidgets(hscores_y_from, hscores_y_to); m_kart_icons[0] = getWidget<IconButtonWidget>("iconscore1");
const int num_laps = m_spinner->getValue(); m_kart_icons[1] = getWidget<IconButtonWidget>("iconscore2");
race_manager->setNumLaps(num_laps); m_kart_icons[2] = getWidget<IconButtonWidget>("iconscore3");
m_highscore_entries[0] = getWidget<LabelWidget>("highscore1");
m_highscore_entries[1] = getWidget<LabelWidget>("highscore2");
m_highscore_entries[2] = getWidget<LabelWidget>("highscore3");
updateHighScores(); updateHighScores();
}
else
{
getWidget<IconButtonWidget>("iconscore1")->setVisible(false);
getWidget<IconButtonWidget>("iconscore2")->setVisible(false);
getWidget<IconButtonWidget>("iconscore3")->setVisible(false);
getWidget<LabelWidget>("highscores")->setVisible(false);
getWidget<LabelWidget>("highscore1")->setVisible(false);
getWidget<LabelWidget>("highscore2")->setVisible(false);
getWidget<LabelWidget>("highscore3")->setVisible(false);
} }
getWidget<ButtonWidget>("start")->setFocusForPlayer( PLAYER_ID_GAME_MASTER );
okBtn->setFocusForPlayer( PLAYER_ID_GAME_MASTER ); } // TrackInfoDialog
}
// ------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------
@ -221,45 +171,7 @@ TrackInfoDialog::~TrackInfoDialog()
((TracksScreen*)curr_screen)->setFocusOnTrack(m_ribbon_item); ((TracksScreen*)curr_screen)->setFocusOnTrack(m_ribbon_item);
} }
} } // ~TrackInfoDialog
// ------------------------------------------------------------------------------------------------------
void TrackInfoDialog::addHighScoreWidgets(const int hscores_y_from, const int hscores_y_to)
{
ITexture* texture = irr_driver->getTexture( (file_manager->getGUIDir() + "/random_kart.png").c_str() ) ;
core::rect< s32 > hiscores_title_area(5, hscores_y_from, m_area.getWidth()/2, hscores_y_from + 30);
stringw text = _("= Highscores =");
IGUIStaticText* hscores_header = GUIEngine::getGUIEnv()->addStaticText( text.c_str(), hiscores_title_area,
false , true , // border, word warp
m_irrlicht_window);
hscores_header->setTextRestrainedInside(false);
hscores_header->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
// fill highscore entries
for (int n=0; n<HIGHSCORE_COUNT; n++)
{
const int from_y = hscores_y_from + (hscores_y_to - hscores_y_from)*(n+1)/(HIGHSCORE_COUNT+1);
const int next_from_y = hscores_y_from + (hscores_y_to - hscores_y_from)*(n+2)/(HIGHSCORE_COUNT+1);
const int gap = 3;
const int icon_size = next_from_y - from_y - gap*2;
core::rect< s32 > icon_area(5, from_y + gap, 5 + icon_size, from_y + icon_size);
m_kart_icons[n] = GUIEngine::getGUIEnv()->addImage( icon_area, m_irrlicht_window );
m_kart_icons[n]->setImage(texture);
m_kart_icons[n]->setScaleImage(true);
m_kart_icons[n]->setTabStop(false);
m_kart_icons[n]->setUseAlphaChannel(true);
core::rect< s32 > entry_area(icon_size + 10, from_y, m_area.getWidth()/2, next_from_y);
m_highscore_entries[n] = GUIEngine::getGUIEnv()->addStaticText( L"", entry_area,
false , true , // border, word warp
m_irrlicht_window);
}
}
// ------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------
@ -305,7 +217,6 @@ void TrackInfoDialog::updateHighScores()
{ {
//I18N: for empty highscores entries //I18N: for empty highscores entries
line = _("(Empty)"); line = _("(Empty)");
line += "\n";
ITexture* no_kart_texture = irr_driver->getTexture( ITexture* no_kart_texture = irr_driver->getTexture(
(file_manager->getGUIDir() + "/random_kart.png").c_str() ) ; (file_manager->getGUIDir() + "/random_kart.png").c_str() ) ;
@ -313,23 +224,27 @@ void TrackInfoDialog::updateHighScores()
} }
m_highscore_entries[n]->setText( line.c_str() ); m_highscore_entries[n]->setText( line.c_str(), false );
} }
} } // updateHighScores
// ------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------
void TrackInfoDialog::onEnterPressedInternal() void TrackInfoDialog::onEnterPressedInternal()
{ {
ModalDialog::dismiss();
// Create a copy of member variables we still need, since they will
// not be accessible after dismiss:
const int num_laps = (m_spinner == NULL ? -1 : m_spinner->getValue()); const int num_laps = (m_spinner == NULL ? -1 : m_spinner->getValue());
const bool reverse_track = m_checkbox == NULL ? false const bool reverse_track = m_checkbox == NULL ? false
: m_checkbox->getState(); : m_checkbox->getState();
race_manager->setReverseTrack(reverse_track); race_manager->setReverseTrack(reverse_track);
race_manager->startSingleRace(m_track_ident, num_laps); printf("Reverse: %d\n", reverse_track);
} std::string track_ident = m_track_ident;
ModalDialog::dismiss();
race_manager->startSingleRace(track_ident, num_laps);
} // onEnterPressedInternal
// ------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------
@ -337,12 +252,7 @@ GUIEngine::EventPropagation TrackInfoDialog::processEvent(const std::string& eve
{ {
if (eventSource == "start" ) if (eventSource == "start" )
{ {
// Create a copy of member variables we still need, since they will onEnterPressedInternal();
// not be accessible after dismiss:
const int num_laps = (m_spinner == NULL ? -1 : m_spinner->getValue());
std::string track_ident = m_track_ident;
ModalDialog::dismiss();
race_manager->startSingleRace(track_ident, num_laps);
return GUIEngine::EVENT_BLOCK; return GUIEngine::EVENT_BLOCK;
} }
else if (eventSource == "reversecheckbox") else if (eventSource == "reversecheckbox")
@ -359,6 +269,6 @@ GUIEngine::EventPropagation TrackInfoDialog::processEvent(const std::string& eve
} }
return GUIEngine::EVENT_LET; return GUIEngine::EVENT_LET;
} } // processEvent
// ------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------

View File

@ -20,12 +20,16 @@
#define HEADER_TRACKINFO_DIALOG_HPP #define HEADER_TRACKINFO_DIALOG_HPP
#include "guiengine/modaldialog.hpp" #include "guiengine/modaldialog.hpp"
#include "guiengine/widgets/spinner_widget.hpp"
#include "guiengine/widgets/check_box_widget.hpp" #include "guiengine/widgets/check_box_widget.hpp"
static const int HIGHSCORE_COUNT = 3; static const int HIGHSCORE_COUNT = 3;
namespace irr { namespace gui { class IGUIImage; class IGUIStaticText; } } namespace GUIEngine
{
class SpinnerWidget;
class IconButtonWidget;
class LabelWidget;
}
/** /**
* \brief Dialog that shows the information about a given track * \brief Dialog that shows the information about a given track
@ -40,10 +44,9 @@ class TrackInfoDialog : public GUIEngine::ModalDialog
// irrlicht labels (more complicated uses require the use of our widget set) // irrlicht labels (more complicated uses require the use of our widget set)
GUIEngine::SpinnerWidget* m_spinner; GUIEngine::SpinnerWidget* m_spinner;
GUIEngine::CheckBoxWidget* m_checkbox; GUIEngine::CheckBoxWidget* m_checkbox;
irr::gui::IGUIImage* m_kart_icons[HIGHSCORE_COUNT]; GUIEngine::IconButtonWidget* m_kart_icons[HIGHSCORE_COUNT];
irr::gui::IGUIStaticText* m_highscore_entries[HIGHSCORE_COUNT]; GUIEngine::LabelWidget* m_highscore_entries[HIGHSCORE_COUNT];
void addHighScoreWidgets(const int hscores_y_from, const int hscores_y_to);
void updateHighScores(); void updateHighScores();
public: public:

View File

@ -54,6 +54,18 @@ DEFINE_SCREEN_SINGLETON( FeatureUnlockedCutScene );
#pragma mark FeatureUnlockedCutScene::UnlockedThing #pragma mark FeatureUnlockedCutScene::UnlockedThing
#endif #endif
FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(std::string model,
irr::core::stringw msg)
{
m_unlocked_kart = NULL;
m_unlock_message = msg;
m_unlock_model = model;
m_curr_image = -1;
}
// -------------------------------------------------------------------------------------
FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(KartProperties* kart, FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(KartProperties* kart,
irr::core::stringw msg) irr::core::stringw msg)
{ {
@ -125,6 +137,48 @@ void FeatureUnlockedCutScene::loadedFromFile()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty)
{
core::stringw msg;
switch (difficulty)
{
case RaceManager::RD_EASY:
msg = _("You completed the easy challenge!");
break;
case RaceManager::RD_MEDIUM:
msg = _("You completed the intermediate challenge!");
break;
case RaceManager::RD_HARD:
msg = _("You completed the difficult challenge!");
break;
default:
assert(false);
}
std::string model;
switch (difficulty)
{
case RaceManager::RD_EASY:
model = file_manager->getModelFile("trophy_bronze.b3d");
break;
case RaceManager::RD_MEDIUM:
model = file_manager->getModelFile("trophy_silver.b3d");
break;
case RaceManager::RD_HARD:
model = file_manager->getModelFile("trophy_gold.b3d");
break;
default:
assert(false);
return;
}
m_unlocked_stuff.push_back( new UnlockedThing(model, msg) );
}
// ----------------------------------------------------------------------------
// unused for now, maybe will be useful later?
/*
void FeatureUnlockedCutScene::addUnlockedKart(KartProperties* unlocked_kart, void FeatureUnlockedCutScene::addUnlockedKart(KartProperties* unlocked_kart,
irr::core::stringw msg) irr::core::stringw msg)
{ {
@ -159,7 +213,7 @@ void FeatureUnlockedCutScene::addUnlockedPictures(std::vector<irr::video::ITextu
m_unlocked_stuff.push_back( new UnlockedThing(pictures, w, h, msg) ); m_unlocked_stuff.push_back( new UnlockedThing(pictures, w, h, msg) );
} // addUnlockedPictures } // addUnlockedPictures
*/
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
const float CAMERA_INITIAL_X = 0.0f; const float CAMERA_INITIAL_X = 0.0f;
@ -232,7 +286,11 @@ void FeatureUnlockedCutScene::init()
m_all_kart_models.clearAndDeleteAll(); m_all_kart_models.clearAndDeleteAll();
for (int n=0; n<unlockedStuffCount; n++) for (int n=0; n<unlockedStuffCount; n++)
{ {
if (m_unlocked_stuff[n].m_unlocked_kart != NULL) if (m_unlocked_stuff[n].m_unlock_model.size() > 0)
{
m_unlocked_stuff[n].m_root_gift_node = irr_driver->addMesh( irr_driver->getMesh(m_unlocked_stuff[n].m_unlock_model) );
}
else if (m_unlocked_stuff[n].m_unlocked_kart != NULL)
{ {
KartModel *kart_model = KartModel *kart_model =
m_unlocked_stuff[n].m_unlocked_kart->getKartModelCopy(); m_unlocked_stuff[n].m_unlocked_kart->getKartModelCopy();
@ -499,6 +557,8 @@ void FeatureUnlockedCutScene::onUpdate(float dt,
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// unused for now... maybe this could could useful later?
/*
void FeatureUnlockedCutScene::addUnlockedThings(const std::vector<const ChallengeData*> unlocked) void FeatureUnlockedCutScene::addUnlockedThings(const std::vector<const ChallengeData*> unlocked)
{ {
for (unsigned int n=0; n<unlocked.size(); n++) for (unsigned int n=0; n<unlocked.size(); n++)
@ -613,6 +673,7 @@ void FeatureUnlockedCutScene::addUnlockedThings(const std::vector<const Challeng
} // next feature } // next feature
} // next challenge } // next challenge
} // addUnlockedThings } // addUnlockedThings
*/
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -21,6 +21,7 @@
#include "graphics/irr_driver.hpp" #include "graphics/irr_driver.hpp"
#include "guiengine/screen.hpp" #include "guiengine/screen.hpp"
#include "race/race_manager.hpp"
#include "utils/ptr_vector.hpp" #include "utils/ptr_vector.hpp"
namespace irr { namespace irr {
@ -47,6 +48,8 @@ class FeatureUnlockedCutScene : public GUIEngine::Screen, public GUIEngine::Scre
/** Will be non-null if this unlocked thing is a kart */ /** Will be non-null if this unlocked thing is a kart */
KartProperties* m_unlocked_kart; KartProperties* m_unlocked_kart;
std::string m_unlock_model;
/** Will be non-empty if this unlocked thing is one or many pictures */ /** Will be non-empty if this unlocked thing is one or many pictures */
std::vector<irr::video::ITexture*> m_pictures; std::vector<irr::video::ITexture*> m_pictures;
/** Will be set if this unlocked thing is a picture */ /** Will be set if this unlocked thing is a picture */
@ -59,6 +62,8 @@ class FeatureUnlockedCutScene : public GUIEngine::Screen, public GUIEngine::Scre
irr::core::stringw m_unlock_message; irr::core::stringw m_unlock_message;
UnlockedThing(std::string model, irr::core::stringw msg);
UnlockedThing(KartProperties* kart, irr::core::stringw msg); UnlockedThing(KartProperties* kart, irr::core::stringw msg);
/** /**
@ -136,21 +141,28 @@ public:
/** Call before showing up the screen to make a kart come out of the chest. /** Call before showing up the screen to make a kart come out of the chest.
'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */ 'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */
void addUnlockedKart(KartProperties* unlocked_kart, irr::core::stringw msg); // unused for now, maybe will be useful later?
//void addUnlockedKart(KartProperties* unlocked_kart, irr::core::stringw msg);
/** Call before showing up the screen to make a picture come out of the chest /** Call before showing up the screen to make a picture come out of the chest
'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */ 'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */
void addUnlockedPicture(irr::video::ITexture* picture, float w, float h, irr::core::stringw msg); // unused for now, maybe will be useful later?
//void addUnlockedPicture(irr::video::ITexture* picture, float w, float h, irr::core::stringw msg);
/** Call before showing up the screen to make a picture slideshow come out of the chest /** Call before showing up the screen to make a picture slideshow come out of the chest
'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */ 'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */
void addUnlockedPictures(std::vector<irr::video::ITexture*> pictures, // unused for now, maybe will be useful later?
float w, float h, irr::core::stringw msg); //void addUnlockedPictures(std::vector<irr::video::ITexture*> pictures,
// float w, float h, irr::core::stringw msg);
/** Call before showing up the screen to make whatever the passed challenges unlocked /** Call before showing up the screen to make whatever the passed challenges unlocked
* come out of the chest */ * come out of the chest */
// unused for now... maybe this could could useful later?
/*
void addUnlockedThings(const std::vector<const ChallengeData*> unlocked); void addUnlockedThings(const std::vector<const ChallengeData*> unlocked);
*/
void addTrophy(RaceManager::Difficulty difficulty);
/** override from base class to handle escape press */ /** override from base class to handle escape press */
virtual bool onEscapePressed(); virtual bool onEscapePressed();

View File

@ -271,17 +271,17 @@ void GrandPrixLose::eventCallback(GUIEngine::Widget* widget,
// un-set the GP mode so that after unlocking, it doesn't try to continue the GP // un-set the GP mode so that after unlocking, it doesn't try to continue the GP
race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE); race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE);
if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0)
{ {
std::vector<const ChallengeData*> unlocked = std::vector<const ChallengeData*> unlocked =
unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges();
unlock_manager->getCurrentSlot()->clearUnlocked(); unlock_manager->getCurrentSlot()->clearUnlocked();
FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance(); FeatureUnlockedCutScene::getInstance();
assert(unlocked.size() > 0); assert(unlocked.size() > 0);
scene->addUnlockedThings(unlocked); scene->addTrophy(race_manager->getDifficulty());
StateManager::get()->replaceTopMostScreen(scene); StateManager::get()->replaceTopMostScreen(scene);
} }

View File

@ -95,7 +95,7 @@ void GrandPrixWin::loadedFromFile()
void GrandPrixWin::init() void GrandPrixWin::init()
{ {
Screen::init(); Screen::init();
if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0)
{ {
const core::dimension2d<u32>& frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize(); const core::dimension2d<u32>& frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize();
@ -393,17 +393,17 @@ void GrandPrixWin::eventCallback(GUIEngine::Widget* widget,
// un-set the GP mode so that after unlocking, it doesn't try to continue the GP // un-set the GP mode so that after unlocking, it doesn't try to continue the GP
race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE); race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE);
if (unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size() > 0) if (unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size() > 0)
{ {
std::vector<const ChallengeData*> unlocked = std::vector<const ChallengeData*> unlocked =
unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges();
unlock_manager->getCurrentSlot()->clearUnlocked(); unlock_manager->getCurrentSlot()->clearUnlocked();
FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance(); FeatureUnlockedCutScene::getInstance();
assert(unlocked.size() > 0); assert(unlocked.size() > 0);
scene->addUnlockedThings(unlocked); scene->addTrophy(race_manager->getDifficulty());
StateManager::get()->replaceTopMostScreen(scene); StateManager::get()->replaceTopMostScreen(scene);
} }

View File

@ -36,6 +36,7 @@
#include "items/item_manager.hpp" #include "items/item_manager.hpp"
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "karts/kart_properties_manager.hpp" #include "karts/kart_properties_manager.hpp"
#include "modes/overworld.hpp"
#include "states_screens/race_setup_screen.hpp" #include "states_screens/race_setup_screen.hpp"
#include "states_screens/state_manager.hpp" #include "states_screens/state_manager.hpp"
#include "utils/translation.hpp" #include "utils/translation.hpp"
@ -1597,8 +1598,16 @@ void KartSelectionScreen::eventCallback(Widget* widget,
playerConfirm(playerID); playerConfirm(playerID);
} }
else if (name == "back") else if (name == "back")
{ {
StateManager::get()->escapePressed(); if (m_from_overworld)
{
m_from_overworld = false; // valid once
OverWorld::enterOverWorld();
}
else
{
StateManager::get()->escapePressed();
}
} }
else else
{ {
@ -1625,6 +1634,22 @@ void KartSelectionScreen::setMultiplayer(bool multiplayer)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool KartSelectionScreen::onEscapePressed()
{
if (m_from_overworld)
{
m_from_overworld = false; // valid once
OverWorld::enterOverWorld();
return false;
}
else
{
return true;
}
}
// ----------------------------------------------------------------------------
#if 0 #if 0
#pragma mark - #pragma mark -
#pragma mark KartSelectionScreen (private) #pragma mark KartSelectionScreen (private)
@ -1747,7 +1772,16 @@ void KartSelectionScreen::allPlayersDone()
input_manager->getDeviceList()->setSinglePlayer( NULL ); input_manager->getDeviceList()->setSinglePlayer( NULL );
} }
StateManager::get()->pushScreen( RaceSetupScreen::getInstance() ); // ---- Go to next screen or return to overworld
if (m_from_overworld)
{
m_from_overworld = false; // valid once
OverWorld::enterOverWorld();
}
else
{
StateManager::get()->pushScreen( RaceSetupScreen::getInstance() );
}
} // allPlayersDone } // allPlayersDone
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -51,6 +51,9 @@ class KartSelectionScreen : public GUIEngine::Screen,
bool m_multiplayer; bool m_multiplayer;
/** Whether this screen is being visited from overworld or not */
bool m_from_overworld;
KartSelectionScreen(); KartSelectionScreen();
/** Stores whether any player confirmed their choice; then, some things /** Stores whether any player confirmed their choice; then, some things
@ -93,6 +96,9 @@ public:
void setMultiplayer(bool multiplayer); void setMultiplayer(bool multiplayer);
/** \brief Set whether this screen is being visited from overworld or not */
void setFromOverworld(bool from_overworld) { m_from_overworld = from_overworld; }
/** \brief Called when a player hits 'fire'/'select' on his device to /** \brief Called when a player hits 'fire'/'select' on his device to
* join the game */ * join the game */
bool playerJoin(InputDevice* device, bool firstPlayer); bool playerJoin(InputDevice* device, bool firstPlayer);
@ -122,4 +128,8 @@ public:
/** \brief implement optional callback from parent /** \brief implement optional callback from parent
* class GUIEngine::Screen */ * class GUIEngine::Screen */
virtual void unloaded(); virtual void unloaded();
/** \brief implement optional callback from parent
* class GUIEngine::Screen */
virtual bool onEscapePressed();
}; };

View File

@ -256,6 +256,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
{ {
KartSelectionScreen* s = KartSelectionScreen::getInstance(); KartSelectionScreen* s = KartSelectionScreen::getInstance();
s->setMultiplayer(false); s->setMultiplayer(false);
s->setFromOverworld(false);
StateManager::get()->pushScreen( s ); StateManager::get()->pushScreen( s );
} }
else if (selection == "multiplayer") else if (selection == "multiplayer")
@ -283,35 +284,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
} }
else if (selection == "story") else if (selection == "story")
{ {
/* OverWorld::enterOverWorld();
StateManager::get()->pushScreen(ChallengesScreen::getInstance());
*/
race_manager->setNumLocalPlayers(1);
race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE);
race_manager->setMinorMode (RaceManager::MINOR_MODE_OVERWORLD);
race_manager->setNumKarts( 1 );
race_manager->setTrack( "overworld" );
race_manager->setDifficulty(RaceManager::RD_HARD);
// Use keyboard 0 by default (FIXME: let player choose?)
InputDevice* device = input_manager->getDeviceList()->getKeyboard(0);
// Create player and associate player with keyboard
StateManager::get()->createActivePlayer(
UserConfigParams::m_all_players.get(0), device );
race_manager->setLocalKartInfo(0, "tux");
// ASSIGN should make sure that only input from assigned devices
// is read.
input_manager->getDeviceList()->setAssignMode(ASSIGN);
input_manager->getDeviceList()
->setSinglePlayer( StateManager::get()->getActivePlayer(0) );
StateManager::get()->enterGameState();
network_manager->setupPlayerKartInfo();
race_manager->startNew();
} }
else if (selection == "tutorial") else if (selection == "tutorial")
{ {

View File

@ -51,7 +51,9 @@ using namespace irr;
const int LOCKED = 0; const int LOCKED = 0;
const int OPEN = 1; const int OPEN = 1;
const int COMPLETED = 2; const int COMPLETED_EASY = 2;
const int COMPLETED_MEDIUM = 3;
const int COMPLETED_HARD = 4;
/** The constructor is called before anything is attached to the scene node. /** The constructor is called before anything is attached to the scene node.
* So rendering to a texture can be done here. But world is not yet fully * So rendering to a texture can be done here. But world is not yet fully
@ -60,8 +62,10 @@ const int COMPLETED = 2;
RaceGUIOverworld::RaceGUIOverworld() RaceGUIOverworld::RaceGUIOverworld()
{ {
m_enabled = true; m_enabled = true;
m_trophy = irr_driver->getTexture( file_manager->getTextureFile("cup_gold.png") ); m_trophy1 = irr_driver->getTexture( file_manager->getTextureFile("cup_bronze.png") );
m_trophy2 = irr_driver->getTexture( file_manager->getTextureFile("cup_silver.png") );
m_trophy3 = irr_driver->getTexture( file_manager->getTextureFile("cup_gold.png") );
const float scaling = irr_driver->getFrameSize().Height / 420.0f; const float scaling = irr_driver->getFrameSize().Height / 420.0f;
// Marker texture has to be power-of-two for (old) OpenGL compliance // 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_rendered_size = 2 << ((int) ceil(1.0 + log(32.0 * scaling)));
@ -101,18 +105,14 @@ RaceGUIOverworld::RaceGUIOverworld()
gui::ScalableFont* font = GUIEngine::getFont(); gui::ScalableFont* font = GUIEngine::getFont();
m_trophy_points_width = font->getDimension(L"100").Width; m_trophy_points_width = font->getDimension(L"100").Width;
const std::vector<const ChallengeData*>& v = unlock_manager->getCurrentSlot()->getLockedChallenges();
for (unsigned int n=0; n<v.size(); n++)
{
m_locked_challenges.insert(v[n]);
}
m_lock = irr_driver->getTexture( file_manager->getTextureFile("gui_lock.png") ); m_lock = irr_driver->getTexture( file_manager->getTextureFile("gui_lock.png") );
m_open_challenge = irr_driver->getTexture( file_manager->getGUIDir() + "challenge.png" ); m_open_challenge = irr_driver->getTexture( file_manager->getGUIDir() + "challenge.png" );
m_icons[0] = m_lock; m_icons[0] = m_lock;
m_icons[1] = m_open_challenge; m_icons[1] = m_open_challenge;
m_icons[2] = m_trophy; m_icons[2] = m_trophy1;
m_icons[3] = m_trophy2;
m_icons[4] = m_trophy3;
} // RaceGUIOverworld } // RaceGUIOverworld
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -217,9 +217,9 @@ void RaceGUIOverworld::drawTrophyPoints()
const int size = UserConfigParams::m_width/20; const int size = UserConfigParams::m_width/20;
core::rect<s32> dest(pos.UpperLeftCorner.X - size - 5, pos.UpperLeftCorner.Y, core::rect<s32> dest(pos.UpperLeftCorner.X - size - 5, pos.UpperLeftCorner.Y,
pos.UpperLeftCorner.X - 5, pos.UpperLeftCorner.Y + size); pos.UpperLeftCorner.X - 5, pos.UpperLeftCorner.Y + size);
core::rect<s32> source(core::position2di(0, 0), m_trophy->getSize()); core::rect<s32> source(core::position2di(0, 0), m_trophy3->getSize());
irr_driver->getVideoDriver()->draw2DImage(m_trophy, dest, source, NULL, irr_driver->getVideoDriver()->draw2DImage(m_trophy3, dest, source, NULL,
NULL, true /* alpha */); NULL, true /* alpha */);
pos.LowerRightCorner.Y = dest.LowerRightCorner.Y; pos.LowerRightCorner.Y = dest.LowerRightCorner.Y;
@ -321,8 +321,10 @@ void RaceGUIOverworld::drawGlobalMiniMap()
int state = (challenges[n].m_force_field.m_is_locked ? LOCKED : OPEN); int state = (challenges[n].m_force_field.m_is_locked ? LOCKED : OPEN);
const Challenge* c = unlock_manager->getCurrentSlot()->getChallenge(challenges[n].m_challenge_id); const Challenge* c = unlock_manager->getCurrentSlot()->getChallenge(challenges[n].m_challenge_id);
if (c->isSolved()) state = COMPLETED; if (c->isSolved(RaceManager::RD_HARD)) state = COMPLETED_HARD;
else if (c->isSolved(RaceManager::RD_MEDIUM)) state = COMPLETED_MEDIUM;
else if (c->isSolved(RaceManager::RD_EASY)) state = COMPLETED_EASY;
const core::rect<s32> source(core::position2d<s32>(0,0), const core::rect<s32> source(core::position2d<s32>(0,0),
m_icons[state]->getOriginalSize()); m_icons[state]->getOriginalSize());

View File

@ -61,11 +61,13 @@ private:
/** The mini map of the track. */ /** The mini map of the track. */
video::ITexture *m_mini_map; video::ITexture *m_mini_map;
video::ITexture *m_trophy; video::ITexture *m_trophy1;
video::ITexture *m_trophy2;
video::ITexture *m_trophy3;
video::ITexture *m_lock; video::ITexture *m_lock;
video::ITexture *m_open_challenge; video::ITexture *m_open_challenge;
video::ITexture* m_icons[3]; video::ITexture* m_icons[5];
/** The size of a single marker on the screen for AI karts, /** The size of a single marker on the screen for AI karts,
* need not be a power of 2. */ * need not be a power of 2. */
@ -95,8 +97,6 @@ private:
int m_trophy_points_width; int m_trophy_points_width;
std::set<const ChallengeData*> m_locked_challenges;
/* Display informat for one player on the screen. */ /* Display informat for one player on the screen. */
void drawEnergyMeter (int x, int y, const Kart *kart, void drawEnergyMeter (int x, int y, const Kart *kart,
const core::recti &viewport, const core::recti &viewport,

View File

@ -90,7 +90,7 @@ void RaceResultGUI::enableAllButtons()
// If something was unlocked // If something was unlocked
// ------------------------- // -------------------------
int n = unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size(); int n = unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size();
if(n>0) if(n>0)
{ {
top->setText(n==1 ? _("See unlocked feature") top->setText(n==1 ? _("See unlocked feature")
@ -137,7 +137,7 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
// If something was unlocked, the 'continue' button was // If something was unlocked, the 'continue' button was
// actually used to display "Show unlocked feature(s)" text. // actually used to display "Show unlocked feature(s)" text.
// --------------------------------------------------------- // ---------------------------------------------------------
int n = unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size(); int n = unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size();
if(n>0) if(n>0)
{ {
if(name=="top") if(name=="top")
@ -148,11 +148,11 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
} }
std::vector<const ChallengeData*> unlocked = std::vector<const ChallengeData*> unlocked =
unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures(); unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges();
unlock_manager->getCurrentSlot()->clearUnlocked(); unlock_manager->getCurrentSlot()->clearUnlocked();
FeatureUnlockedCutScene* scene = FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance(); FeatureUnlockedCutScene::getInstance();
scene->addUnlockedThings(unlocked); scene->addTrophy(race_manager->getDifficulty());
StateManager::get()->popMenu(); StateManager::get()->popMenu();
StateManager::get()->pushScreen(scene); StateManager::get()->pushScreen(scene);
World::deleteWorld(); World::deleteWorld();

View File

@ -190,6 +190,14 @@ void Track::cleanup()
} }
m_all_cached_meshes.clear(); m_all_cached_meshes.clear();
// Now free meshes that are not associated to any scene node.
for (unsigned int i=0; i<m_detached_cached_meshes.size(); i++)
{
irr_driver->dropAllTextures(m_detached_cached_meshes[i]);
irr_driver->removeMeshFromCache(m_detached_cached_meshes[i]);
}
m_detached_cached_meshes.clear();
QuadGraph::destroy(); QuadGraph::destroy();
if(m_mini_map) if(m_mini_map)
@ -459,6 +467,12 @@ void Track::convertTrackToBullet(scene::ISceneNode *node)
if (node->getType() == scene::ESNT_LOD_NODE) if (node->getType() == scene::ESNT_LOD_NODE)
{ {
node = ((LODNode*)node)->getFirstNode(); node = ((LODNode*)node)->getFirstNode();
if (node == NULL)
{
fprintf(stderr, "[Track] WARNING: this track contains an empty LOD group : '%s'\n",
((LODNode*)node)->getGroupName().c_str());
return;
}
} }
const core::vector3df &pos = node->getPosition(); const core::vector3df &pos = node->getPosition();
@ -810,7 +824,7 @@ bool Track::loadMainTrack(const XMLNode &root)
std::string challenge; std::string challenge;
n->get("challenge", &challenge); n->get("challenge", &challenge);
lodLoader.check(n); bool is_lod = lodLoader.check(n);
if (tangent) if (tangent)
{ {
@ -818,6 +832,13 @@ bool Track::loadMainTrack(const XMLNode &root)
scene::IMesh* original_mesh = irr_driver->getMesh(full_path); scene::IMesh* original_mesh = irr_driver->getMesh(full_path);
if (std::find(m_detached_cached_meshes.begin(),
m_detached_cached_meshes.end(),
original_mesh) == m_detached_cached_meshes.end())
{
m_detached_cached_meshes.push_back(original_mesh);
}
// create a node out of this mesh just for bullet; delete it after, normal maps are special // create a node out of this mesh just for bullet; delete it after, normal maps are special
// and require tangent meshes // and require tangent meshes
scene_node = irr_driver->addMesh(original_mesh); scene_node = irr_driver->addMesh(original_mesh);
@ -828,10 +849,12 @@ bool Track::loadMainTrack(const XMLNode &root)
convertTrackToBullet(scene_node); convertTrackToBullet(scene_node);
scene_node->remove(); scene_node->remove();
irr_driver->grabAllTextures(original_mesh);
scene::IMesh* mesh = manip->createMeshWithTangents(original_mesh); scene::IMesh* mesh = manip->createMeshWithTangents(original_mesh);
mesh->grab(); mesh->grab();
irr_driver->grabAllTextures(mesh); irr_driver->grabAllTextures(mesh);
m_all_cached_meshes.push_back(mesh); m_all_cached_meshes.push_back(mesh);
scene_node = irr_driver->addMesh(mesh); scene_node = irr_driver->addMesh(mesh);
scene_node->setPosition(xyz); scene_node->setPosition(xyz);
@ -846,7 +869,7 @@ bool Track::loadMainTrack(const XMLNode &root)
handleAnimatedTextures(scene_node, *n); handleAnimatedTextures(scene_node, *n);
m_all_nodes.push_back( scene_node ); m_all_nodes.push_back( scene_node );
} }
else if (lodLoader.check(n)) else if (is_lod)
{ {
// nothing to do // nothing to do
} }

View File

@ -136,6 +136,12 @@ private:
* that those meshes are being cached by irrlicht, and need to be freed. */ * that those meshes are being cached by irrlicht, and need to be freed. */
std::vector<scene::IMesh*> m_all_cached_meshes; std::vector<scene::IMesh*> m_all_cached_meshes;
/**
* m_all_cached_meshes assumes meshes are attached to a scene node.
* This one assumes the mesh is NOT connected to any node.
*/
std::vector<scene::IMesh*> m_detached_cached_meshes;
/** A list of all textures loaded by the track, so that they can /** A list of all textures loaded by the track, so that they can
* be removed from the cache at cleanup time. */ * be removed from the cache at cleanup time. */
std::vector<video::ITexture*> m_all_cached_textures; std::vector<video::ITexture*> m_all_cached_textures;

View File

@ -25,6 +25,7 @@
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "io/xml_node.hpp" #include "io/xml_node.hpp"
#include "items/item_manager.hpp" #include "items/item_manager.hpp"
#include "modes/overworld.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "tracks/track.hpp" #include "tracks/track.hpp"
@ -44,6 +45,8 @@ TrackObject::TrackObject(const XMLNode &xml_node)
m_enabled = true; m_enabled = true;
m_is_looped = false; m_is_looped = false;
m_sound = NULL; m_sound = NULL;
m_mesh = NULL;
m_node = NULL;
xml_node.get("xyz", &m_init_xyz ); xml_node.get("xyz", &m_init_xyz );
xml_node.get("hpr", &m_init_hpr ); xml_node.get("hpr", &m_init_hpr );
@ -57,12 +60,12 @@ TrackObject::TrackObject(const XMLNode &xml_node)
xml_node.get("lod_group", &m_lod_group); xml_node.get("lod_group", &m_lod_group);
/** For sound effects */ std::string type;
bool trigger_when_near = false; xml_node.get("type", &type );
/** For sound effects */
float trigger_distance = 1.0f;
bool trigger_when_near = false;
float trigger_distance = 1.0f;
// FIXME: at this time sound emitters are just disabled in multiplayer // FIXME: at this time sound emitters are just disabled in multiplayer
// otherwise the sounds would be constantly heard // otherwise the sounds would be constantly heard
if (sound.size() > 0 && race_manager->getNumLocalPlayers() == 1) if (sound.size() > 0 && race_manager->getNumLocalPlayers() == 1)
@ -105,14 +108,27 @@ TrackObject::TrackObject(const XMLNode &xml_node)
"[TrackObject] Sound emitter object could not be created\n"); "[TrackObject] Sound emitter object could not be created\n");
} }
} }
else if (type == "action-trigger")
{
trigger_when_near = true;
xml_node.get("distance", &trigger_distance);
xml_node.get("action", &m_action);
if (m_action.size() == 0)
{
fprintf(stderr, "[TrackObject] WARNING: action-trigger has no action defined\n");
}
}
// Some animated objects (billboards, sound emitters) // Some animated objects (billboards, sound emitters, action triggers)
// don't use this scene node // don't use this scene node
if (model_name == "") if (model_name == "")
{ {
m_node = NULL; m_node = NULL;
m_mesh = NULL; m_mesh = NULL;
if (trigger_when_near) if (trigger_when_near)
{ {
item_manager->newItem(m_init_xyz, trigger_distance, this); item_manager->newItem(m_init_xyz, trigger_distance, this);
@ -122,6 +138,7 @@ TrackObject::TrackObject(const XMLNode &xml_node)
{ {
std::string full_path = std::string full_path =
World::getWorld()->getTrack()->getTrackFile(model_name); World::getWorld()->getTrack()->getTrackFile(model_name);
if(file_manager->fileExists(full_path)) if(file_manager->fileExists(full_path))
{ {
m_mesh = irr_driver->getAnimatedMesh(full_path); m_mesh = irr_driver->getAnimatedMesh(full_path);
@ -132,13 +149,11 @@ TrackObject::TrackObject(const XMLNode &xml_node)
// in STK's model directory. // in STK's model directory.
full_path = file_manager->getModelFile(model_name); full_path = file_manager->getModelFile(model_name);
m_mesh = irr_driver->getAnimatedMesh(full_path); m_mesh = irr_driver->getAnimatedMesh(full_path);
if(!m_mesh) if(!m_mesh)
{ {
fprintf(stderr, throw std::runtime_error("Model '" + model_name + "' cannot be found");
"Warning: '%s' in '%s' not found and is ignored.\n", }
xml_node.getName().c_str(), model_name.c_str());
return;
} // if(!m_mesh)
} }
m_mesh->grab(); m_mesh->grab();
@ -295,4 +310,16 @@ void TrackObject::onTriggerItemApproached(Item* who)
{ {
m_sound->play(); m_sound->play();
} }
else if (m_action.size() > 0)
{
if (m_action == "garage")
{
dynamic_cast<OverWorld*>(World::getWorld())->scheduleReturnToGarage();
}
else
{
fprintf(stderr, "[TrackObject] WARNING: unknown action <%s>\n",
m_action.c_str());
}
}
} }

View File

@ -88,6 +88,9 @@ protected:
/** LOD group this object is part of, if it is LOD */ /** LOD group this object is part of, if it is LOD */
std::string m_lod_group; std::string m_lod_group;
/** For action trigger objects */
std::string m_action;
public: public:
TrackObject(const XMLNode &xml_node); TrackObject(const XMLNode &xml_node);
TrackObject(const core::vector3df& pos, const core::vector3df& hpr, TrackObject(const core::vector3df& pos, const core::vector3df& hpr,

View File

@ -47,46 +47,58 @@ TrackObjectManager::~TrackObjectManager()
*/ */
void TrackObjectManager::add(const XMLNode &xml_node) void TrackObjectManager::add(const XMLNode &xml_node)
{ {
std::string groupname; try
xml_node.get("lod_group", &groupname);
bool is_lod = !groupname.empty();
std::string type;
xml_node.get("type", &type);
if(type=="movable")
{ {
if (is_lod) std::string groupname;
xml_node.get("lod_group", &groupname);
bool is_lod = !groupname.empty();
std::string type;
xml_node.get("type", &type);
if(type=="movable")
{ {
m_lod_objects[groupname].push_back(new PhysicalObject(xml_node)); if (is_lod)
{
m_lod_objects[groupname].push_back(new PhysicalObject(xml_node));
}
else
{
m_all_objects.push_back(new PhysicalObject(xml_node));
}
}
else if(type=="animation")
{
if (is_lod)
{
m_lod_objects[groupname].push_back(new ThreeDAnimation(xml_node));
}
else
{
m_all_objects.push_back(new ThreeDAnimation(xml_node));
}
}
else if(type=="billboard")
{
m_all_objects.push_back(new BillboardAnimation(xml_node));
}
else if(type=="sfx-emitter")
{
m_all_objects.push_back(new TrackObject(xml_node));
}
else if(type=="action-trigger")
{
m_all_objects.push_back(new TrackObject(xml_node));
} }
else else
{ {
m_all_objects.push_back(new PhysicalObject(xml_node)); fprintf(stderr, "Unknown track object: '%s' - ignored.\n",
type.c_str());
} }
} }
else if(type=="animation") catch (std::exception& e)
{ {
if (is_lod) fprintf(stderr, "[TrackObjectManager] WARNING: Could not load track object. Reason : %s\n",
{ e.what());
m_lod_objects[groupname].push_back(new ThreeDAnimation(xml_node));
}
else
{
m_all_objects.push_back(new ThreeDAnimation(xml_node));
}
}
else if(type=="billboard")
{
m_all_objects.push_back(new BillboardAnimation(xml_node));
}
else if(type=="sfx-emitter")
{
m_all_objects.push_back(new TrackObject(xml_node));
}
else
{
fprintf(stderr, "Unknown track object: '%s' - ignored.\n",
type.c_str());
} }
} // add } // add