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
cmake_minimum_required(VERSION 2.8.1)
include (CheckCXXSourceCompiles)
project(SuperTuxKart)
@ -118,8 +119,24 @@ else()
if(HAVE_IRRLICHT)
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})
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()
message(FATAL_ERROR "\n -- Irrlicht not found (can't locate irrlicht.h)\n Use -DIRRLICHT_DIR=/path/to/irrlicht")
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
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
ADD_DEPENDENCIES(supertuxkart GenerateDesktopFile)

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@ -6,19 +6,31 @@
<spacer height="2%" width="1"/>
<icon-button id="novice" icon="gui/difficulty_easy.png"
I18N="Difficulty" text="Novice" proportion="1"/>
<div width="100%" proportion="1" layout="horizontal-row">
<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"/>
<icon-button id="intermediate" icon="gui/difficulty_medium.png"
I18N="Difficulty" text="Intermediate" proportion="1"/>
<div width="100%" proportion="1" layout="horizontal-row">
<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"/>
<icon-button id="expert" icon="gui/difficulty_hard.png"
I18N="Difficulty" text="Expert" proportion="1"/>
<div width="100%" proportion="1" layout="horizontal-row">
<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"/>
</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.
As an example, a value of 1 for x and z will result in a
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" />
<!-- 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());
return;
}
// See if the challenge is solved (it's activated later from the
// unlock_manager).
bool finished=false;
node->get("solved", &finished);
const XMLNode* easy = node->getNode("easy");
const XMLNode* medium = node->getNode("medium");
const XMLNode* hard = node->getNode("hard");
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
//-----------------------------------------------------------------------------
const wchar_t* boolstr(bool b)
{
return (b ? L"true" : L"false");
}
void Challenge::save(XMLWriter& writer)
{
writer << L" <" << core::stringw(m_data->getId().c_str()) << L" solved=\""
<< (isSolved() ? L"true" : L"false") << L"\"";
writer << L" />\n";
writer << L" <" << core::stringw(m_data->getId().c_str()) << L">\n"
<< L" <easy solved=\"" << boolstr(isSolved(RaceManager::RD_EASY)) << 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

View File

@ -30,6 +30,7 @@
#include <fstream>
#include <irrString.h>
#include "race/race_manager.hpp"
#include "utils/no_copy.hpp"
#include "utils/translation.hpp"
@ -46,26 +47,38 @@ class Challenge : public NoCopy
private:
enum {CH_INACTIVE, // challenge not yet possible
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;
public:
Challenge(ChallengeData* data) : m_state(CH_INACTIVE)
{ m_data = data; }
Challenge(ChallengeData* 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() {};
void load(const XMLNode* config);
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

View File

@ -356,12 +356,13 @@ bool ChallengeData::raceFinished()
int d = race_manager->getDifficulty();
Kart* kart = world->getPlayerKart(0);
if (track_name != m_track_id ) 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_position[d] > 0 && kart->getPosition() > m_position[d]) return false;
// Follow the 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();
} // 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()
@ -70,21 +41,53 @@ void GameSlot::computeActive()
{
// Changed challenge
// -----------------
if((i->second)->isSolved())
if((i->second)->isSolvedAtAnyDifficulty())
{
// The constructor calls computeActive, which actually locks
// all features, so unlock the solved ones (and don't try to
// 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++;
continue;
}
else
{
// Otherwise lock the feature
// --------------------------
lockFeature(i->second);
}
// Otherwise lock the feature
// --------------------------
lockFeature(i->second);
i->second->setActive();
if (i->second->isSolved(RaceManager::RD_HARD))
{
// challenge beaten at hardest, nothing more to do here
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
clearUnlocked();
} // 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();
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
m_unlocked_features.push_back(c->getData());
c->setSolved(); // reset isActive flag
c->setSolved(d); // reset isActive flag
// Save the new unlock information
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
*/
void GameSlot::raceFinished()
@ -156,9 +141,9 @@ void GameSlot::raceFinished()
for(i = m_challenges_state.begin();
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
}
//race_manager->setCoinTarget(0); //reset
@ -172,10 +157,11 @@ void GameSlot::grandPrixFinished()
for(i = m_challenges_state.begin();
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");
unlockFeature(i->second);
unlockFeature(i->second, race_manager->getDifficulty());
}
}
race_manager->setCoinTarget(0);

View File

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

View File

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

View File

@ -92,7 +92,11 @@ public:
void add(int level, scene::ISceneNode* node, bool reparent);
/** 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; }

View File

@ -75,28 +75,34 @@ public:
class WaterShaderProvider : public video::IShaderConstantSetCallBack
{
float m_dx_1, m_dy_1, m_dx_2, m_dy_2;
float m_water_shader_speed_1;
float m_water_shader_speed_2;
public:
LEAK_CHECK()
WaterShaderProvider()
WaterShaderProvider(float water_shader_speed_1,
float water_shader_speed_2)
{
m_dx_1 = 0.0f;
m_dy_1 = 0.0f;
m_dx_2 = 0.0f;
m_dy_2 = 0.0f;
m_water_shader_speed_1 = water_shader_speed_1/100.0f;
m_water_shader_speed_2 = water_shader_speed_2/100.0f;
}
virtual void OnSetConstants(
irr::video::IMaterialRendererServices *services,
s32 userData)
{
m_dx_1 += GUIEngine::getLatestDt()/15.0f;
m_dy_1 += GUIEngine::getLatestDt()/15.0f;
m_dx_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1;
m_dy_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1;
m_dx_2 += GUIEngine::getLatestDt()/25.0f;
m_dy_2 -= GUIEngine::getLatestDt()/25.0f;
m_dx_2 += GUIEngine::getLatestDt()*m_water_shader_speed_2;
m_dy_2 -= GUIEngine::getLatestDt()*m_water_shader_speed_2;
if (m_dx_1 > 1.0f) m_dx_1 -= 1.0f;
if (m_dy_1 > 1.0f) m_dy_1 -= 1.0f;
@ -429,6 +435,11 @@ Material::Material(const XMLNode *node, int index)
}
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
const unsigned int children_count = node->getNumNodes();
@ -505,6 +516,8 @@ void Material::init(unsigned int index)
m_add = false;
m_disable_z_write = false;
m_water_shader = false;
m_water_shader_speed_1 = 6.6667f;
m_water_shader_speed_2 = 4.0f;
m_fog = true;
m_max_speed_fraction = 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)
{
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")));

View File

@ -107,7 +107,18 @@ private:
bool m_zipper;
/** If a kart is rescued when driving on this surface. */
bool m_drive_reset;
/** If the water shader (simulating wave effects and reflexions) is enabled */
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. */
CollisionReaction m_collision_reaction;

View File

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

View File

@ -372,6 +372,9 @@ namespace GUIEngine
Another possible value is "fit", which will make a \<div\> fit to its
contents.
Another possible value is "font", which will use the size of the font
(useful to insert widgets inside text)
\n
\subsection prop9 PROP_MAX_WIDTH, PROP_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
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
\subsection prop13 PROP_PROPORTION
<em> Name in XML files: </em> \c "proportion"

View File

@ -259,7 +259,8 @@ void LayoutManager::readCoords(Widget* self)
// width
{
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;
else if (percent_w > -1) self->m_relative_w = (float)percent_w;
@ -271,7 +272,8 @@ void LayoutManager::readCoords(Widget* self)
// height
{
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;
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; }
void setMin(int n) { m_min = n; }
/** Override method from base class Widget */
virtual void setActivated();

View File

@ -292,14 +292,52 @@ void Attachment::hitBanana(Item *item, int new_attachment)
} // hitBanana
//-----------------------------------------------------------------------------
//** Moves a bomb from kart FROM to kart TO.
void Attachment::moveBombFromTo(Kart *from, Kart *to)
/** Updates the attachments in case of a kart-kart collision. This must only
* 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,
from->getAttachment()->getTimeLeft()+
stk_config->m_bomb_time_increase, from);
from->getAttachment()->clear();
} // moveBombFromTo
Attachment *attachment_other=other->getAttachment();
if(getType()==Attachment::ATTACH_BOMB)
{
// 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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,6 +42,7 @@
#include "karts/kart_gfx.hpp"
#include "modes/world.hpp"
#include "io/file_manager.hpp"
#include "items/attachment.hpp"
#include "items/item_manager.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/kart_model.hpp"
@ -69,7 +70,8 @@
* \param is_first_kart Indicates whether this is the first *player* 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)
: TerrainInfo(1),
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_initial_position = position;
m_race_position = position;
m_world_kart_id = world_kart_id;
m_collected_energy = 0;
m_finished_race = false;
m_wheel_toggle = 1;
m_finish_time = 0.0f;
m_bubblegum_time = 0.0f;
m_invulnerable_time = 0.0f;
m_squash_time = 0.0f;
m_shadow_enabled = false;
m_shadow = NULL;
m_terrain_particles = NULL;
m_collision_particles = NULL;
m_slipstream = NULL;
m_skidmarks = NULL;
@ -430,7 +431,6 @@ Kart::~Kart()
delete m_kart_gfx;
if(m_terrain_sound) sfx_manager->deleteSFX(m_terrain_sound);
if(m_previous_terrain_sound) sfx_manager->deleteSFX(m_previous_terrain_sound);
if(m_terrain_particles) delete m_terrain_particles;
if(m_collision_particles) delete m_collision_particles;
if(m_slipstream) delete m_slipstream;
if(m_rain) delete m_rain;
@ -906,7 +906,6 @@ void Kart::update(float dt)
//smoke drawing control point
if (UserConfigParams::m_graphical_effects)
{
if (m_terrain_particles) m_terrain_particles->update(dt);
if (m_rain)
{
m_rain->setPosition( getCamera()->getCameraSceneNode()->getPosition() );
@ -1153,140 +1152,92 @@ void Kart::handleMaterialGFX()
// on top of a surface (i.e. not falling), actually touching
// something with the wheels, and the material has not the
// 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;
m_wheel_toggle = 1 - m_wheel_toggle;
const btWheelInfo &wi =
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);
// Disable potentially running particle effects
m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
return; // no particle effect, return
}
m_kart_gfx->updateTerrain(pk);
return;
}
// Now the kart is either falling, or driving on a terrain which
// has the 'below surface' flag set. Detect if there is a surface
// on top of the kart.
// --------------------------------------------------------------
if (m_camera && m_camera->getMode() != Camera::CM_FINAL)
{
if (material && material->hasFallingEffect() && !m_flying)
{
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);
}
}
} // camera != final camera
if (m_terrain_particles)
{
// 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;
if (!UserConfigParams::m_graphical_effects)
return;
float create;
if (distance < 2.0f)
{
create = (float)pk->getMaxRate();
}
else if (distance < 4.0f)
{
create = pk->getMinRate() + (pk->getMaxRate() - pk->getMinRate())*(distance - 2.0f)/2.0f;
}
else
{
create = 0.0f;
}
m_terrain_particles->setCreationRateAbsolute(create);
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);
}
// handleMaterialSFX(surface_material);
}
// 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_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
return;
}
const ParticleKind *pk =
surface_material->getParticlesWhen(Material::EMIT_ON_DRIVE);
if(!pk || m_flying || m_kart_mode == EA_RESCUE)
return;
// Now the kart is under a surface, and there is a surface effect
// --------------------------------------------------------------
m_kart_gfx->setParticleKind(KartGFX::KGFX_TERRAIN, pk);
m_kart_gfx->setXYZ(KartGFX::KGFX_TERRAIN, xyz);
const float distance = xyz.distance2(from);
float ratio;
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
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
@ -1313,7 +1264,7 @@ void Kart::setCamera(Camera *camera)
{
m_rain->setCamera( camera->getCameraSceneNode() );
}
}
} // setCamera
//-----------------------------------------------------------------------------
/** Sets zipper time, and apply one time additional speed boost. It can be
@ -1406,44 +1357,64 @@ void Kart::setSlipstreamEffect(float f)
} // setSlipstreamEffect
// -----------------------------------------------------------------------------
/** Called when the kart crashes against the track (k=NULL) or another kart.
* \param k Either a kart if a kart was hit, or NULL if the track was hit.
* \param m
/** Called when the kart crashes against another kart.
* \param k The kart that was hit.
* \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
// Simple debug output for people playing without sound.
// This makes it easier to see if a kart hit the track (esp.
// after a jump).
// 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
// output was added.
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
m_controller->crashed();
/** If a kart is crashing against the track, the collision is often
* reported more than once, resulting in a machine gun effect, and too
* long disabling of the engine. Therefore, this reaction is disabled
* 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();
if (particles.size() > 0)
{
ParticleKind* kind = ParticleKindManager::get()->getParticles(particles);
ParticleKind* kind =
ParticleKindManager::get()->getParticles(particles);
if (kind != NULL)
{
if (m_collision_particles == NULL)
{
Vec3 position(-getKartWidth()*0.35f, 0.06f, getKartLength()*0.5f);
m_collision_particles = new ParticleEmitter(kind, position, getNode());
Vec3 position(-getKartWidth()*0.35f, 0.06f,
getKartLength()*0.5f);
m_collision_particles =
new ParticleEmitter(kind, position, getNode());
}
else
{
@ -1465,14 +1436,24 @@ void Kart::crashed(Kart *k, const Material *m)
{
if (m_bounce_back_time <= 0.0f)
{
btVector3 push = m_vehicle->getRigidBody()->getLinearVelocity().normalized();
push[1] = 0.1f;
m_vehicle->getRigidBody()->applyCentralImpulse( -4000.0f*push );
//m_vehicle->getRigidBody()->setLinearVelocity( -m_vehicle->getRigidBody()->getLinearVelocity() );
m_bounce_back_time = 2.0f;
btVector3 push = m_body->getLinearVelocity().normalized();
push[1] = 0.1f;
m_body->applyCentralImpulse( -4000.0f*push );
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;
m_time_last_crash = World::getWorld()->getTime();
@ -2028,25 +2009,6 @@ void Kart::loadData(RaceManager::KartType type, bool is_first_kart,
createPhysics();
// 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 &&
track->getSkyParticles() != NULL)

View File

@ -28,7 +28,6 @@
#include "LinearMath/btTransform.h"
#include "items/attachment.hpp"
#include "items/powerup.hpp"
#include "karts/controller/controller.hpp"
#include "karts/controller/kart_control.hpp"
@ -42,6 +41,7 @@
class btKart;
class btUprightConstraint;
class Attachment;
class Camera;
class Item;
class KartGFX;
@ -114,9 +114,6 @@ private:
* determine startup boost. */
bool m_has_started;
/** For skidding smoke */
int m_wheel_toggle;
/**<Maximum engine rpm's for the current gear*/
float m_max_gear_rpm;
@ -154,9 +151,6 @@ private:
* stuck to the kart, i.e. the shadow would be flying, too). */
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;
/** All particle effects. */
@ -211,6 +205,7 @@ private:
void updateEngineSFX();
float getVisualSkidOffset() const;
void crashed();
protected:
const KartProperties *m_kart_properties;
@ -220,11 +215,10 @@ protected:
KartModel* m_kart_model;
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);
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,
bool animatedModel);
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 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 finishedRace (float time);
@ -257,6 +252,9 @@ public:
bool playCustomSFX (unsigned int type);
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. */
KartModel* getKartModel() { return m_kart_model; }
// ------------------------------------------------------------------------

View File

@ -22,45 +22,45 @@
#include "io/file_manager.hpp"
#include "graphics/particle_emitter.hpp"
#include "graphics/particle_kind.hpp"
#include "graphics/particle_kind_manager.hpp"
#include "karts/kart.hpp"
#include "physics/btKart.hpp"
#include <iostream>
KartGFX::KartGFX(const Kart *kart) : m_current_skid(KGFX_SKID1)
KartGFX::KartGFX(const Kart *kart)
{
if(!UserConfigParams::m_graphical_effects)
{
for(unsigned int i=0; i<KGFX_COUNT; i++)
{
m_all_emitters.push_back(NULL);
m_all_particle_kinds.push_back(NULL);
}
return;
}
m_kart = kart;
Vec3 position(0, kart->getKartHeight()*0.35f,
-kart->getKartLength()*0.35f);
Vec3 rear_center(0, kart->getKartHeight()*0.35f,
-kart->getKartLength()*0.35f);
// Create all effects. Note that they must be created
// in the order of KartGFXType.
addEffect(KGFX_NITRO, "nitro.xml", position);
addEffect(KGFX_ZIPPER, "zipper_fire.xml", position);
addEffect(KGFX_SKID1, "skid1.xml", position);
addEffect(KGFX_SKID2, "skid2.xml", position);
addEffect(KGFX_NITRO, "nitro.xml", rear_center);
addEffect(KGFX_ZIPPER, "zipper_fire.xml", rear_center);
addEffect(KGFX_TERRAIN, "smoke.xml", Vec3(0,0,0));
addEffect(KGFX_SKID1, "skid1.xml", rear_center);
addEffect(KGFX_SKID2, "skid2.xml", rear_center);
} // KartGFX
// ----------------------------------------------------------------------------
/** Destructor. Frees all particle effects and kinds.
*/
KartGFX::~KartGFX()
{
for(unsigned int i=0; i<KGFX_COUNT; i++)
{
if(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
} // ~KartGFX
@ -75,17 +75,23 @@ KartGFX::~KartGFX()
void KartGFX::addEffect(KartGFXType type, const std::string &file_name,
const Vec3 &position)
{
ParticleKind *kind = NULL;
ParticleEmitter *emitter = NULL;
const ParticleKind *kind = NULL;
ParticleEmitter *emitter = NULL;
try
{
kind = new ParticleKind(file_manager->getGfxFile(file_name));
if(type==KGFX_SKID2)
emitter = NULL; // skid2 is only used to store the emitter type
kind = ParticleKindManager::get()->getParticles(file_name);
//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
emitter = new ParticleEmitter(kind,
position,
m_kart->getNode());
emitter = new ParticleEmitter(kind, position, m_kart->getNode());
}
catch (std::runtime_error& e)
{
@ -97,15 +103,20 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name,
kind = NULL;
emitter = NULL;
}
assert(m_all_emitters.size()==type);
assert((int)m_all_emitters.size()==type);
m_all_emitters.push_back(emitter);
assert(m_all_particle_kinds.size()==type);
m_all_particle_kinds.push_back(kind);
if(type==KGFX_SKID1)
m_skid_kind1 = kind;
else if (type==KGFX_SKID2)
m_skid_kind2 = kind;
} // addEffect
// ----------------------------------------------------------------------------
/** Resets all particle emitters. Used at the (re)start of a race.
*/
void KartGFX::reset()
{
m_wheel_toggle = 1;
for(unsigned int i=0; i<m_all_emitters.size(); 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
* 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(type == KGFX_SKID1 || type==KGFX_SKID2);
m_current_skid = type;
assert(level >= 1);
assert(level <= 2);
const ParticleKind *pk = level==1 ? m_skid_kind1 : m_skid_kind2;
if(m_all_emitters[KGFX_SKID1])
m_all_emitters[KGFX_SKID1]->setParticleType(
m_all_particle_kinds[type]);
m_all_emitters[KGFX_SKID1]->setParticleType(pk);
} // setSkidLevel
// ----------------------------------------------------------------------------
/** Updates all gfx.
* \param dt Time step size.
/** Sets a new particle type to be used. Note that the memory of this
* 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++)
{
if(m_all_emitters[i])
m_all_emitters[i]->update(dt);
}
} // update
pe->setParticleType(pk);
} // setParticleKind
// ----------------------------------------------------------------------------
/** Sets the creation rate for the specified particle type relative to the
* given minimum and maximum particle rate.
* \param type The particle effect for which to set the
* creation rate.
* \param f The new relative creation rate.
/** Defines the new position of the specified emitter.
* \param type The emitter to set a new position for.
* \param xyz The new position of the emitter.
*/
void KartGFX::setCreationRateRelative(KartGFXType type, float f)
void KartGFX::setXYZ(const KartGFXType type, const Vec3 &xyz)
{
if(m_all_emitters[type])
m_all_emitters[type]->setCreationRateRelative(f);
} // setCreationRate
ParticleEmitter *pe = m_all_emitters[KGFX_TERRAIN];
if(!pe) return;
pe->setPosition(xyz);
} // setXYZ
// ----------------------------------------------------------------------------
/** 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])
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
@ -185,4 +211,63 @@ void KartGFX::resizeBox(KartGFXType type, float speed, float dt)
{
if(m_all_emitters[type])
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:
/** 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. */
enum KartGFXType { KGFX_NITRO=0,
KGFX_ZIPPER,
KGFX_TERRAIN,
KGFX_SKID,
KGFX_SKID1=KGFX_SKID,
KGFX_SKID2,
KGFX_COUNT};
private:
/** Vector of all particle kinde. */
std::vector<ParticleKind*> m_all_particle_kinds;
/** The particle kind for skidding bonus level 1. */
const ParticleKind *m_skid_kind1;
/** The particle kind for skidding bonus level 2. */
const ParticleKind *m_skid_kind2;
/** Vector of all particle emitters. */
std::vector<ParticleEmitter*> m_all_emitters;
@ -53,8 +60,8 @@ private:
/** Pointer to the owner of this kart. */
const Kart *m_kart;
/** Indicates the current skidding level, either skid1 or skid2. */
KartGFXType m_current_skid;
/** Used to alternate particle effects from the rear wheels. */
int m_wheel_toggle;
void addEffect(KartGFXType type, const std::string &file_name,
const Vec3 &position);
@ -63,10 +70,13 @@ public:
KartGFX(const Kart *kart);
~KartGFX();
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 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
#endif

View File

@ -20,11 +20,12 @@
#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,
const btTransform& init_transform,
RaceManager::KartType type)
: Kart(ident, track, position, is_first_kart,
: Kart(ident, world_kart_id, track, position, is_first_kart,
init_transform, type)
{
reset();

View File

@ -65,7 +65,8 @@ private:
float m_skidding_time;
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,
const btTransform& init_transform,
RaceManager::KartType type);

View File

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

View File

@ -20,15 +20,57 @@
#include "input/input.hpp"
#include "input/input_manager.hpp"
#include "karts/kart.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/overworld.hpp"
#include "network/network_manager.hpp"
#include "states_screens/dialogs/select_challenge.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/race_gui_overworld.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()
{
m_return_to_garage = false;
}
// ----------------------------------------------------------------------------
@ -56,11 +98,22 @@ void OverWorld::update(float dt)
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++)
{
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
//-----------------------------------------------------------------------------

View File

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

View File

@ -91,8 +91,12 @@ Kart *ProfileWorld::createKart(const std::string &kart_ident, int index,
{
btTransform init_pos = m_track->getStartTransform(index);
Kart *new_kart = new KartWithStats(kart_ident, m_track, index+1,
false, init_pos,
Kart *new_kart = new KartWithStats(kart_ident,
/*world kart id*/ index,
m_track,
/*position*/ index+1,
/*is_first_kart*/false,
init_pos,
RaceManager::KT_AI);
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_schedule_pause = false;
m_schedule_unpause = false;
m_self_destruct = false;
WorldStatus::setClockMode(CLOCK_CHRONO);
} // World
@ -144,7 +145,6 @@ void World::init()
Kart* newkart = createKart(kart_ident, i, local_player_id,
global_player_id);
m_karts.push_back(newkart);
newkart->setWorldKartId(m_karts.size()-1);
m_track->adjustForFog(newkart->getNode());
} // for i
@ -190,7 +190,7 @@ Kart *World::createKart(const std::string &kart_ident, int index,
{
int position = index+1;
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,
race_manager->getKartType(index));
Controller *controller = NULL;
@ -590,6 +590,12 @@ void World::updateWorld(float dt)
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.
if( m_phase == FINISH_PHASE ||
m_phase == IN_GAME_MENU_PHASE )
@ -600,7 +606,6 @@ void World::updateWorld(float dt)
{
enterRaceOverState();
}
} // updateWorld
#define MEASURE_FPS 0
@ -948,4 +953,13 @@ void World::unpause()
}
} // 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 */

View File

@ -146,6 +146,11 @@ protected:
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:
World();
virtual ~World();
@ -276,10 +281,12 @@ public:
// ------------------------------------------------------------------------
/** Override if you want to know when a kart presses fire */
virtual void onFirePressed(Controller* who) {}
// ------------------------------------------------------------------------
/** Whether to compute checkline requirements for each world on the
* quadgraph. Override to change value. */
virtual bool useChecklineRequirements() const { return false; }
// ------------------------------------------------------------------------
void delayedSelfDestruct();
}; // World

View File

@ -23,10 +23,13 @@
/** A network kart. On the server, it receives its control information (steering etc)
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,
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;
} // NetworkKart

View File

@ -28,7 +28,8 @@ class NetworkKart : public Kart
private:
int m_global_player_id; // to identify this kart to the network manager
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,
int global_player_id, RaceManager::KartType type);
void setControl(const KartControl& kc);

View File

@ -438,8 +438,11 @@ void btKart::updateVehicle( btScalar step )
if(m_time_additional_impulse>0)
{
m_time_additional_impulse -= step;
m_chassisBody->applyCentralImpulse(m_additional_impulse);
float dt = step > m_time_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)
@ -461,7 +464,7 @@ void btKart::updateVehicle( btScalar step )
// kart, or a strongly 'visual jolt' of the kart
btTransform &iwt=m_chassisBody->getInterpolationWorldTransform();
iwt.setRotation(iwt.getRotation()*add_rot);
m_time_additional_rotation -= step;
m_time_additional_rotation -= dt;
}
} // updateVehicle
@ -724,30 +727,33 @@ void btKart::updateFriction(btScalar timeStep)
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;
btScalar x = (m_forwardImpulse[wheel] ) * fwdFactor;
btScalar y = (m_sideImpulse[wheel] ) * sideFactor;
btScalar impulseSquared = (x*x + y*y);
if (impulseSquared > maximpSquared)
if(m_time_additional_impulse>0)
{
sliding = true;
btScalar factor = maximp / btSqrt(impulseSquared);
m_wheelInfo[wheel].m_skidInfo *= factor;
} // if impulseSquared > maximpSquared
m_wheelInfo[wheel].m_skidInfo = 0.0f;
}
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_velocity = 0;
@ -779,7 +785,7 @@ void btKart::updateFriction(btScalar timeStep)
av.setY(m_skid_angular_velocity);
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++)
{

View File

@ -230,7 +230,7 @@ public:
} // setTimedImpulse
// ------------------------------------------------------------------------
/** 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
* 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,
const Vec3 &contact_point)
{
if(contact_point.getX()>0)
return COL_RIGHT;
else
return COL_LEFT;
btVector3 aabb_min, aabb_max;
static btTransform zero_trans(btQuaternion(0, 0, 0));
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
* replay what happened on the server.
* \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 contact_point_b Location of collision at second kart (in kart
* coordinates).
*/
void Physics::KartKartCollision(Kart *kart_a, const Vec3 &contact_point_a,
Kart *kart_b, const Vec3 &contact_point_b)
{
kart_a->crashed(kart_b); // will play crash sound for player karts
kart_b->crashed(kart_a);
Attachment *attachmentA=kart_a->getAttachment();
Attachment *attachmentB=kart_b->getAttachment();
// Only one kart needs to handle the attachments, it will
// fix the attachments for the other kart.
kart_a->crashed(kart_b, /*handle_attachments*/true);
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:
if(attachmentB->getType()==Attachment::ATTACH_BOMB)
{
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);
left_kart = kart_a;
right_kart = kart_b;
}
else
{
kart_a->playCustomSFX(SFXManager::CUSTOM_CRASH);
kart_b->playCustomSFX(SFXManager::CUSTOM_CRASH);
left_kart = kart_b;
right_kart = kart_a;
}
// If bouncing crashes is enabled, add an additional force to the
// slower kart
Kart *faster_kart, *slower_kart;
Vec3 faster_cp, slower_cp;
if(kart_a->getSpeed()>=kart_b->getSpeed())
// Add a scaling factor depending on the mass (avoid div by zero)
float f_right = right_kart->getKartProperties()->getMass() > 0
? left_kart->getKartProperties()->getMass()
/ right_kart->getKartProperties()->getMass()
: 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;
faster_cp = contact_point_a;
slower_kart = kart_b;
slower_cp = contact_point_b;
f_right *= vel_left == 0 ? 2.0f : (1.0f - vel_diff/fabsf(vel_left));
if(f_right > 2.0f)
f_right = 2.0f;
}
else
{
faster_kart = kart_b;
faster_cp = contact_point_b;
slower_kart = kart_a;
slower_cp = contact_point_a;
f_left *= vel_right == 0 ? 2.0f : (1.0f + vel_diff/fabsf(vel_right));
if(f_left > 2.0f)
f_left = 2.0f;
}
CollisionSide faster_side = getCollisionSide(faster_kart->getBody(),
faster_cp);
CollisionSide slower_side = getCollisionSide(slower_kart->getBody(),
slower_cp);
// Increase the effect somewhat by squaring the factors
f_left = f_left * f_left;
f_right = f_right * f_right;
// This probably needs adjusting once we have different kart properties.
// E.g. besides speed we might also want to take mass into account(?)
if(faster_side==COL_FRONT)
// First push one kart to the left (if there is not already
// an impulse happening - one collision might cause more
// than one impulse otherwise)
if(right_kart->getVehicle()->getCentralImpulseTime()<=0)
{
// Special case: the faster kart hits a kart front on. In this case
// the slower kart will be pushed out of the faster kart's way
Vec3 dir = faster_kart->getVelocity();
// The direction in which the impulse will be applied depends on
// which side of the faster kart was hitting it: if the hit is
// on the right side of the faster kart, it will push the slower
// 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?
const KartProperties *kp = left_kart->getKartProperties();
Vec3 impulse(-kp->getCollisionImpulse()*f_left, 0, 0);
impulse = right_kart->getTrans().getBasis() * impulse;
right_kart->getVehicle()
->setTimedCentralImpulse(kp->getCollisionImpulseTime(),
impulse);
right_kart ->getBody()->setAngularVelocity(btVector3(0,0,0));
}
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
// First the faster kart
Vec3 dir = faster_kart->getVelocity();
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 *= 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));
const KartProperties *kp = right_kart->getKartProperties();
Vec3 impulse = Vec3(kp->getCollisionImpulse()*f_right, 0, 0);
impulse = left_kart->getTrans().getBasis() * impulse;
left_kart->getVehicle()
->setTimedCentralImpulse(kp->getCollisionImpulseTime(),
impulse);
left_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
}
} // KartKartCollision
@ -470,7 +458,7 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies,
const Material *m
= n>=0 ? upA->getPointerTriangleMesh()->getMaterial(n)
: NULL;
kart->crashed(NULL, m);
kart->crashed(m);
}
}
// 2) object a is a kart
@ -485,7 +473,7 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies,
const Material *m
= n>=0 ? upB->getPointerTriangleMesh()->getMaterial(n)
: NULL;
kart->crashed(NULL, m); // Kart hit track
kart->crashed(m); // Kart hit track
}
else if(upB->is(UserPointer::UP_FLYABLE))
// 2.1 projectile hits kart

View File

@ -138,7 +138,7 @@ private:
btCollisionDispatcher *m_dispatcher;
btBroadphaseInterface *m_axis_sweep;
btDefaultCollisionConfiguration *m_collision_conf;
CollisionList m_all_collisions;
CollisionList m_all_collisions;
public:
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
// 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;
} // exitRace

View File

@ -483,7 +483,7 @@ public:
* \note In GP, displays the GP result screen first
* \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

View File

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

View File

@ -18,13 +18,47 @@
#include "challenges/unlock_manager.hpp"
#include "config/user_config.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/input_manager.hpp"
#include "io/file_manager.hpp"
#include "modes/world.hpp"
#include "network/network_manager.hpp"
#include "race/race_manager.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,
const float percentHeight,
@ -35,14 +69,61 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth,
m_challenge_id = challenge_id;
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()
{
World::getWorld()->scheduleUnpause();
}
// ----------------------------------------------------------------------------
GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::string& eventSourceParam)
{
std::string eventSource = eventSourceParam;
@ -94,14 +175,17 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin
if (eventSource == "novice")
{
challenge->setRace(RaceManager::RD_EASY);
UserConfigParams::m_difficulty = 0;
}
else if (eventSource == "intermediate")
{
challenge->setRace(RaceManager::RD_MEDIUM);
UserConfigParams::m_difficulty = 1;
}
else if (eventSource == "expert")
{
challenge->setRace(RaceManager::RD_HARD);
UserConfigParams::m_difficulty = 2;
}
else
{
@ -119,3 +203,5 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin
return GUIEngine::EVENT_LET;
}
// ----------------------------------------------------------------------------

View File

@ -20,6 +20,8 @@
#include "guiengine/screen.hpp"
#include "guiengine/widgets/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 "karts/kart_properties_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 float w, const float h) : ModalDialog(w, h)
{
loadFromFile("track_info_dialog.stkgui");
const bool has_laps = race_manager->modeHasLaps();
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_ribbon_item = ribbonItem;
// ---- Track title
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;
getWidget<LabelWidget>("name")->setText(trackName.c_str(), false);
// ---- Track credits
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)
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
Widget* screenshot_div = getWidget("screenshot_div");
IconButtonWidget* screenshotWidget = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
false /* tab stop */, false /* focusable */);
// images are saved squared, but must be stretched to 4:
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_y = area_right.UpperLeftCorner.Y;
screenshotWidget->m_w = area_right.getWidth();
screenshotWidget->m_h = area_right.getHeight();
screenshotWidget->m_x = screenshot_div->m_x;
screenshotWidget->m_y = screenshot_div->m_y;
screenshotWidget->m_w = screenshot_div->m_w;
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)
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);
a->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
b->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
// ---- Lap count m_spinner
if (has_laps)
{
m_spinner = new SpinnerWidget();
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 = getWidget<SpinnerWidget>("lapcountspinner");
m_spinner->m_properties[PROP_ID] = "lapcountspinner";
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)
m_spinner->setText( _("%i laps") );
m_widgets.push_back(m_spinner);
m_spinner->add();
//m_spinner->setText( _("%i laps") );
m_spinner->setValue( UserConfigParams::m_num_laps );
m_spinner->getIrrlichtElement()->setTabStop(true);
m_spinner->getIrrlichtElement()->setTabGroup(false);
//m_spinner->getIrrlichtElement()->setTabStop(true);
//m_spinner->getIrrlichtElement()->setTabGroup(false);
const int num_laps = m_spinner->getValue();
race_manager->setNumLaps(num_laps);
}
else
{
m_spinner = NULL;
}
// Reverse track
const bool reverse_available = track->reverseAvailable();
if (reverse_available)
{
m_checkbox = new CheckBoxWidget();
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 = getWidget<CheckBoxWidget>("reverse");
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
{
@ -181,33 +129,35 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin
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
if (has_highscores)
{
addHighScoreWidgets(hscores_y_from, hscores_y_to);
const int num_laps = m_spinner->getValue();
race_manager->setNumLaps(num_laps);
m_kart_icons[0] = getWidget<IconButtonWidget>("iconscore1");
m_kart_icons[1] = getWidget<IconButtonWidget>("iconscore2");
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();
}
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);
}
}
// ------------------------------------------------------------------------------------------------------
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);
}
}
} // ~TrackInfoDialog
// ------------------------------------------------------------------------------------------------------
@ -305,7 +217,6 @@ void TrackInfoDialog::updateHighScores()
{
//I18N: for empty highscores entries
line = _("(Empty)");
line += "\n";
ITexture* no_kart_texture = irr_driver->getTexture(
(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()
{
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 bool reverse_track = m_checkbox == NULL ? false
: m_checkbox->getState();
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" )
{
// 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());
std::string track_ident = m_track_ident;
ModalDialog::dismiss();
race_manager->startSingleRace(track_ident, num_laps);
onEnterPressedInternal();
return GUIEngine::EVENT_BLOCK;
}
else if (eventSource == "reversecheckbox")
@ -359,6 +269,6 @@ GUIEngine::EventPropagation TrackInfoDialog::processEvent(const std::string& eve
}
return GUIEngine::EVENT_LET;
}
} // processEvent
// ------------------------------------------------------------------------------------------------------

View File

@ -20,12 +20,16 @@
#define HEADER_TRACKINFO_DIALOG_HPP
#include "guiengine/modaldialog.hpp"
#include "guiengine/widgets/spinner_widget.hpp"
#include "guiengine/widgets/check_box_widget.hpp"
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
@ -40,10 +44,9 @@ class TrackInfoDialog : public GUIEngine::ModalDialog
// irrlicht labels (more complicated uses require the use of our widget set)
GUIEngine::SpinnerWidget* m_spinner;
GUIEngine::CheckBoxWidget* m_checkbox;
irr::gui::IGUIImage* m_kart_icons[HIGHSCORE_COUNT];
irr::gui::IGUIStaticText* m_highscore_entries[HIGHSCORE_COUNT];
GUIEngine::IconButtonWidget* m_kart_icons[HIGHSCORE_COUNT];
GUIEngine::LabelWidget* m_highscore_entries[HIGHSCORE_COUNT];
void addHighScoreWidgets(const int hscores_y_from, const int hscores_y_to);
void updateHighScores();
public:

View File

@ -54,6 +54,18 @@ DEFINE_SCREEN_SINGLETON( FeatureUnlockedCutScene );
#pragma mark FeatureUnlockedCutScene::UnlockedThing
#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,
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,
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) );
} // addUnlockedPictures
*/
// ----------------------------------------------------------------------------
const float CAMERA_INITIAL_X = 0.0f;
@ -232,7 +286,11 @@ void FeatureUnlockedCutScene::init()
m_all_kart_models.clearAndDeleteAll();
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 =
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)
{
for (unsigned int n=0; n<unlocked.size(); n++)
@ -613,6 +673,7 @@ void FeatureUnlockedCutScene::addUnlockedThings(const std::vector<const Challeng
} // next feature
} // next challenge
} // addUnlockedThings
*/
// ----------------------------------------------------------------------------

View File

@ -21,6 +21,7 @@
#include "graphics/irr_driver.hpp"
#include "guiengine/screen.hpp"
#include "race/race_manager.hpp"
#include "utils/ptr_vector.hpp"
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 */
KartProperties* m_unlocked_kart;
std::string m_unlock_model;
/** Will be non-empty if this unlocked thing is one or many pictures */
std::vector<irr::video::ITexture*> m_pictures;
/** 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;
UnlockedThing(std::string model, 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.
'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
'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
'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */
void addUnlockedPictures(std::vector<irr::video::ITexture*> pictures,
float w, float h, irr::core::stringw msg);
// unused for now, maybe will be useful later?
//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
* come out of the chest */
// unused for now... maybe this could could useful later?
/*
void addUnlockedThings(const std::vector<const ChallengeData*> unlocked);
*/
void addTrophy(RaceManager::Difficulty difficulty);
/** override from base class to handle escape press */
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
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 =
unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures();
unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges();
unlock_manager->getCurrentSlot()->clearUnlocked();
FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance();
assert(unlocked.size() > 0);
scene->addUnlockedThings(unlocked);
scene->addTrophy(race_manager->getDifficulty());
StateManager::get()->replaceTopMostScreen(scene);
}

View File

@ -95,7 +95,7 @@ void GrandPrixWin::loadedFromFile()
void GrandPrixWin::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();
@ -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
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 =
unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures();
unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges();
unlock_manager->getCurrentSlot()->clearUnlocked();
FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance();
assert(unlocked.size() > 0);
scene->addUnlockedThings(unlocked);
scene->addTrophy(race_manager->getDifficulty());
StateManager::get()->replaceTopMostScreen(scene);
}

View File

@ -36,6 +36,7 @@
#include "items/item_manager.hpp"
#include "io/file_manager.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/overworld.hpp"
#include "states_screens/race_setup_screen.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/translation.hpp"
@ -1597,8 +1598,16 @@ void KartSelectionScreen::eventCallback(Widget* widget,
playerConfirm(playerID);
}
else if (name == "back")
{
StateManager::get()->escapePressed();
{
if (m_from_overworld)
{
m_from_overworld = false; // valid once
OverWorld::enterOverWorld();
}
else
{
StateManager::get()->escapePressed();
}
}
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
#pragma mark -
#pragma mark KartSelectionScreen (private)
@ -1747,7 +1772,16 @@ void KartSelectionScreen::allPlayersDone()
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
// ----------------------------------------------------------------------------

View File

@ -51,6 +51,9 @@ class KartSelectionScreen : public GUIEngine::Screen,
bool m_multiplayer;
/** Whether this screen is being visited from overworld or not */
bool m_from_overworld;
KartSelectionScreen();
/** Stores whether any player confirmed their choice; then, some things
@ -93,6 +96,9 @@ public:
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
* join the game */
bool playerJoin(InputDevice* device, bool firstPlayer);
@ -122,4 +128,8 @@ public:
/** \brief implement optional callback from parent
* class GUIEngine::Screen */
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();
s->setMultiplayer(false);
s->setFromOverworld(false);
StateManager::get()->pushScreen( s );
}
else if (selection == "multiplayer")
@ -283,35 +284,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
}
else if (selection == "story")
{
/*
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();
OverWorld::enterOverWorld();
}
else if (selection == "tutorial")
{

View File

@ -51,7 +51,9 @@ using namespace irr;
const int LOCKED = 0;
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.
* 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()
{
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;
// 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)));
@ -101,18 +105,14 @@ RaceGUIOverworld::RaceGUIOverworld()
gui::ScalableFont* font = GUIEngine::getFont();
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_open_challenge = irr_driver->getTexture( file_manager->getGUIDir() + "challenge.png" );
m_icons[0] = m_lock;
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
//-----------------------------------------------------------------------------
@ -217,9 +217,9 @@ void RaceGUIOverworld::drawTrophyPoints()
const int size = UserConfigParams::m_width/20;
core::rect<s32> dest(pos.UpperLeftCorner.X - size - 5, pos.UpperLeftCorner.Y,
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 */);
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);
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),
m_icons[state]->getOriginalSize());

View File

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

View File

@ -90,7 +90,7 @@ void RaceResultGUI::enableAllButtons()
// If something was unlocked
// -------------------------
int n = unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures().size();
int n = unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges().size();
if(n>0)
{
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
// 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(name=="top")
@ -148,11 +148,11 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
}
std::vector<const ChallengeData*> unlocked =
unlock_manager->getCurrentSlot()->getRecentlyUnlockedFeatures();
unlock_manager->getCurrentSlot()->getRecentlyCompletedChallenges();
unlock_manager->getCurrentSlot()->clearUnlocked();
FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance();
scene->addUnlockedThings(unlocked);
scene->addTrophy(race_manager->getDifficulty());
StateManager::get()->popMenu();
StateManager::get()->pushScreen(scene);
World::deleteWorld();

View File

@ -190,6 +190,14 @@ void Track::cleanup()
}
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();
if(m_mini_map)
@ -459,6 +467,12 @@ void Track::convertTrackToBullet(scene::ISceneNode *node)
if (node->getType() == scene::ESNT_LOD_NODE)
{
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();
@ -810,7 +824,7 @@ bool Track::loadMainTrack(const XMLNode &root)
std::string challenge;
n->get("challenge", &challenge);
lodLoader.check(n);
bool is_lod = lodLoader.check(n);
if (tangent)
{
@ -818,6 +832,13 @@ bool Track::loadMainTrack(const XMLNode &root)
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
// and require tangent meshes
scene_node = irr_driver->addMesh(original_mesh);
@ -828,10 +849,12 @@ bool Track::loadMainTrack(const XMLNode &root)
convertTrackToBullet(scene_node);
scene_node->remove();
irr_driver->grabAllTextures(original_mesh);
scene::IMesh* mesh = manip->createMeshWithTangents(original_mesh);
mesh->grab();
irr_driver->grabAllTextures(mesh);
m_all_cached_meshes.push_back(mesh);
scene_node = irr_driver->addMesh(mesh);
scene_node->setPosition(xyz);
@ -846,7 +869,7 @@ bool Track::loadMainTrack(const XMLNode &root)
handleAnimatedTextures(scene_node, *n);
m_all_nodes.push_back( scene_node );
}
else if (lodLoader.check(n))
else if (is_lod)
{
// nothing to do
}

View File

@ -136,6 +136,12 @@ private:
* that those meshes are being cached by irrlicht, and need to be freed. */
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
* be removed from the cache at cleanup time. */
std::vector<video::ITexture*> m_all_cached_textures;

View File

@ -25,6 +25,7 @@
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "items/item_manager.hpp"
#include "modes/overworld.hpp"
#include "modes/world.hpp"
#include "tracks/track.hpp"
@ -44,6 +45,8 @@ TrackObject::TrackObject(const XMLNode &xml_node)
m_enabled = true;
m_is_looped = false;
m_sound = NULL;
m_mesh = NULL;
m_node = NULL;
xml_node.get("xyz", &m_init_xyz );
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);
/** For sound effects */
bool trigger_when_near = false;
/** For sound effects */
float trigger_distance = 1.0f;
std::string type;
xml_node.get("type", &type );
bool trigger_when_near = false;
float trigger_distance = 1.0f;
// FIXME: at this time sound emitters are just disabled in multiplayer
// otherwise the sounds would be constantly heard
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");
}
}
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
if (model_name == "")
{
m_node = NULL;
m_mesh = NULL;
if (trigger_when_near)
{
item_manager->newItem(m_init_xyz, trigger_distance, this);
@ -122,6 +138,7 @@ TrackObject::TrackObject(const XMLNode &xml_node)
{
std::string full_path =
World::getWorld()->getTrack()->getTrackFile(model_name);
if(file_manager->fileExists(full_path))
{
m_mesh = irr_driver->getAnimatedMesh(full_path);
@ -132,13 +149,11 @@ TrackObject::TrackObject(const XMLNode &xml_node)
// in STK's model directory.
full_path = file_manager->getModelFile(model_name);
m_mesh = irr_driver->getAnimatedMesh(full_path);
if(!m_mesh)
{
fprintf(stderr,
"Warning: '%s' in '%s' not found and is ignored.\n",
xml_node.getName().c_str(), model_name.c_str());
return;
} // if(!m_mesh)
throw std::runtime_error("Model '" + model_name + "' cannot be found");
}
}
m_mesh->grab();
@ -295,4 +310,16 @@ void TrackObject::onTriggerItemApproached(Item* who)
{
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 */
std::string m_lod_group;
/** For action trigger objects */
std::string m_action;
public:
TrackObject(const XMLNode &xml_node);
TrackObject(const core::vector3df& pos, const core::vector3df& hpr,

View File

@ -47,46 +47,58 @@ TrackObjectManager::~TrackObjectManager()
*/
void TrackObjectManager::add(const XMLNode &xml_node)
{
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")
try
{
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
{
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)
{
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());
fprintf(stderr, "[TrackObjectManager] WARNING: Could not load track object. Reason : %s\n",
e.what());
}
} // add