Tutorial development incremental commit

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@7095 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
aeonphyxius 2010-12-20 00:52:59 +00:00
parent a8ae299119
commit 2427aed571
8 changed files with 699 additions and 4 deletions

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<tutorial
id="basicdriving"
name="Win the At World's End Grand Prix"
description="Come first in the At World's End Grand Prix with 3 Expert AI karts."
major="single"
minor="normal"
gp="canyon"
difficulty="easy"
karts="1"
position="1"
num_laps="99"
num_players="1"
/>

@ -502,6 +502,10 @@
RelativePath="..\..\modes\three_strikes_battle.cpp"
>
</File>
<File
RelativePath="..\..\modes\tutorial_race.cpp"
>
</File>
<File
RelativePath="..\..\modes\world.cpp"
>
@ -1118,6 +1122,10 @@
RelativePath="..\..\tutorial\tutorial.cpp"
>
</File>
<File
RelativePath="..\..\tutorial\tutorial_data.cpp"
>
</File>
<File
RelativePath="..\..\tutorial\tutorial_manager.cpp"
>
@ -1420,6 +1428,10 @@
RelativePath="..\..\modes\three_strikes_battle.hpp"
>
</File>
<File
RelativePath="..\..\modes\tutorial_race.hpp"
>
</File>
<File
RelativePath="..\..\modes\world.hpp"
>
@ -2024,6 +2036,10 @@
RelativePath="..\..\tutorial\tutorial.hpp"
>
</File>
<File
RelativePath="..\..\tutorial\tutorial_data.hpp"
>
</File>
<File
RelativePath="..\..\tutorial\tutorial_manager.hpp"
>

194
src/modes/tutorial_race.cpp Normal file

@ -0,0 +1,194 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Alejandro Santiago
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "modes/tutorial_race.hpp"
#include "audio/music_manager.hpp"
#include "tutorial/tutorial_manager.hpp"
#include "config/user_config.hpp"
#include "items/powerup_manager.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/track.hpp"
#include "utils/translation.hpp"
//-----------------------------------------------------------------------------
TutorialRace::TutorialRace() : LinearWorld()
{
// We have to make sure that no kart finished the set number of laps
// in a FTL race (since otherwise its distance will not be computed
// correctly, and as a result e.g. a leader might suddenly fall back
// after crossing the start line
race_manager->setNumLaps(99999);
m_leader_intervals = stk_config->m_leader_intervals;
for(unsigned int i=0; i<m_leader_intervals.size(); i++)
m_leader_intervals[i] +=
stk_config->m_leader_time_per_kart*race_manager->getNumberOfKarts();
m_use_highscores = false; // disable high scores
setClockMode(WorldStatus::CLOCK_COUNTDOWN, m_leader_intervals[0]);
}
//-----------------------------------------------------------------------------
TutorialRace::~TutorialRace()
{
}
#if 0
#pragma mark -
#pragma mark clock events
#endif
//-----------------------------------------------------------------------------
/** Returns the original time at which the countdown timer started. This is
* used by the race_gui to display the music credits in FTL mode correctly.
*/
float TutorialRace::getClockStartTime()
{
return m_leader_intervals[0];
} // getClockStartTime
//-----------------------------------------------------------------------------
/** Called when a kart must be eliminated.
*/
void TutorialRace::countdownReachedZero()
{
if(m_leader_intervals.size()>1)
m_leader_intervals.erase(m_leader_intervals.begin());
WorldStatus::setTime(m_leader_intervals[0]);
// If the leader kart is not the first kart, remove the first
// kart, otherwise remove the last kart.
int position_to_remove = m_karts[0]->getPosition()==1
? getCurrentNumKarts() : 1;
Kart *kart = getKartAtPosition(position_to_remove);
if(!kart || kart->isEliminated())
{
fprintf(stderr,"Problem with removing leader: position %d not found\n",
position_to_remove);
for(unsigned int i=0; i<m_karts.size(); i++)
{
fprintf(stderr,"kart %d: eliminated %d position %d\n",
i,m_karts[i]->isEliminated(), m_karts[i]->getPosition());
} // for i
} //
else
{
removeKart(kart->getWorldKartId());
// In case that the kart on position 1 was removed, we have
// to set the correct position (which equals the remaining
// number of karts) for the removed kart, as well as recompute
// the position for all other karts
if(position_to_remove==1)
{
// We have to add 1 to the number of karts to get the correct
// position, since the eliminated kart was already removed
// from the value returned by getCurrentNumKarts (and we have
// to remove the kart before we can call updateRacePosition).
// Note that we can not call WorldWithRank::setKartPosition
// here, since it would not properly support debugging kart
// ranks (since this kart would get its position set again
// in updateRacePosition). We only set the rank of the eliminated
// kart, and updateRacePosition will then call setKartPosition
// for the now eliminated kart.
kart->setPosition(getCurrentNumKarts()+1);
updateRacePosition();
}
}
// almost over, use fast music
if(getCurrentNumKarts()==3)
{
music_manager->switchToFastMusic();
}
if (isRaceOver())
{
// Handle special FTL situation: the leader is kart number 3 when
// the last kart gets eliminated. In this case kart on position 1
// is eliminated, and the kart formerly on position 2 is on
// position 1, the leader now position 2. In this case the kart
// on position 1 would get more points for this victory. So if
// this is the case, change the position
if(m_karts[0]->getPosition()!=1)
{
// Adjust the position of all still driving karts except
// the leader by +1, and move the leader to position 1.
for (unsigned int i=1; i<m_karts.size(); i++)
{
if(!m_karts[i]->hasFinishedRace() &&
!m_karts[i]->isEliminated() )
m_karts[i]->setPosition(m_karts[i]->getPosition()+1);
}
m_karts[0]->setPosition(1);
}
// Mark all still racing karts to be finished.
for (unsigned int n=0; n<m_karts.size(); n++)
{
if (!m_karts[n]->isEliminated() &&
!m_karts[n]->hasFinishedRace())
{
m_karts[n]->finishedRace(getTime());
}
}
}
// End of race is detected from World::updateWorld()
} // countdownReachedZero
//-----------------------------------------------------------------------------
/** The follow the leader race is over if there is only one kart left (plus
* the leader), or if all (human) players have been eliminated.
*/
bool TutorialRace::isRaceOver()
{
// FIXME : add the logic to detect if the user have ended the tutorial.
return true;
} // isRaceOver
//-----------------------------------------------------------------------------
void TutorialRace::restartRace()
{
LinearWorld::restartRace();
m_leader_intervals.clear();
m_leader_intervals = stk_config->m_leader_intervals;
for(unsigned int i=0; i<m_leader_intervals.size(); i++)
m_leader_intervals[i] +=
stk_config->m_leader_time_per_kart*race_manager->getNumberOfKarts();
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN,
m_leader_intervals[0]);
} // restartRace
//-----------------------------------------------------------------------------
/** Returns the internal identifier for this kind of race.
*/
std::string TutorialRace::getIdent() const
{
return FTL_IDENT;
} // getIdent
//-----------------------------------------------------------------------------
/** Sets the title for all karts that is displayed in the icon list. In
* this mode the title for the first kart is set to 'leader'.
*/
RaceGUIBase::KartIconDisplayInfo* TutorialRace::getKartsDisplayInfo()
{
LinearWorld::getKartsDisplayInfo();
m_kart_display_info[0].special_title = _("Leader");
return m_kart_display_info;
} // getKartsDisplayInfo

@ -0,0 +1,55 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Alejandro Santiago
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_TUTORIAL_MODE_HPP
#define HEADER_TUTORIAL_MODE_HPP
#include "modes/linear_world.hpp"
#include "states_screens/race_gui_base.hpp"
/**
* \brief An implementation of World, based on LinearWorld, to provide the Follow-the-leader game mode
* \ingroup modes
*/
class TutorialRace : public LinearWorld
{
private:
std::vector <float> m_leader_intervals; // time till elimination in follow leader
public:
TutorialRace();
virtual ~TutorialRace();
// clock events
virtual void countdownReachedZero();
// overriding World methods
virtual void restartRace();
virtual std::string getIdent() const;
float getClockStartTime();
virtual bool useFastMusicNearEnd() const { return false; }
virtual RaceGUIBase::KartIconDisplayInfo* getKartsDisplayInfo();
virtual bool isRaceOver();
virtual bool raceHasLaps(){ return false; }
};
#endif //HEADER_TUTORIAL_MODE_HPP

@ -25,7 +25,7 @@
using namespace irr::core;
const std::string BASIC_DRIVING = "bd";
const std::string BASIC_DRIVING = "basicdriving";
const std::string SHARP_TURN = "st";
const std::string NITRO = "nt";
const std::string COLLECTIBLE_WEAPONS = "cw";

@ -66,7 +66,7 @@ private:
std::string m_Id; // short, internal name for this tutorial
irr::core::stringw m_Name; // name used in menu for this tutorial
irr::core::stringw m_challenge_description; // Message the user gets when the feature is not yet unlocked
irr::core::stringw m_tutorial_description; // Message the user gets when the feature is not yet unlocked
// vector<UnlockableFeature> m_feature; // Features to unlock
// vector<string> m_prerequisites; // what needs to be done before accessing this tutorial
@ -83,6 +83,8 @@ public:
void addUnlockTrackReward(const std::string &track_name);
void addUnlockModeReward(const std::string &internal_mode_name,
const irr::core::stringw &user_mode_name);
void setTutorialDescription(const irr::core::stringw& d)
{m_tutorial_description=d; }
/* void addUnlockGPReward(const std::string &gp_name);
void addUnlockDifficultyReward(const std::string &internal_name,
const irr::core::stringw &user_name);
@ -91,8 +93,7 @@ public:
/*const vector<UnlockableFeature>&
getFeatures() const { return m_feature; }
void setChallengeDescription(const irr::core::stringw& d)
{m_challenge_description=d; }
const irr::core::stringw&
getChallengeDescription() const {return m_challenge_description; }
void addDependency(const std::string id) {m_prerequisites.push_back(id); }

@ -0,0 +1,339 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Alejandro Santiago
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "tutorial/tutorial_data.hpp"
#include <stdexcept>
#include <sstream>
#include "karts/kart.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/linear_world.hpp"
#include "race/grand_prix_data.hpp"
#include "race/grand_prix_manager.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/translation.hpp"
TutorialData::TutorialData(const std::string& filename)
#ifndef WIN32
throw(std::runtime_error)
#endif
{
m_filename = filename;
m_major = RaceManager::MAJOR_MODE_SINGLE;
m_minor = RaceManager::MINOR_MODE_NORMAL_RACE;
m_difficulty = RaceManager::RD_EASY;
m_num_laps = -1;
m_num_karts = -1;
m_time = -1.0f;
m_track_name = "";
m_gp_id = "";
m_energy = -1;
XMLNode *root = new XMLNode( filename );
// Check if the file have been correctly load
if(!root || root->getName()!="tutorial")
{
delete root;
std::ostringstream msg;
msg << "Couldn't load tutorial '" << filename << "': no tutorial node.";
throw std::runtime_error(msg.str());
}
std::string s_property;
int i_property;
float f_property;
// Mode
root->get("major", &s_property);
setMajor(s_property);
// Minor
root->get("minor", &s_property);
setMinor(s_property);
// Name
if(!root->get("name", &s_property) )
error("name");
setName( _(s_property.c_str()) );
// ID
if(!root->get("id", &s_property) )
error("id");
setId(s_property);
// Description
if(!root->get("s_property", &s_property) )
error("description");
setTutorialDescription( _(s_property.c_str()) );
// Karts
if(!root->get("karts", &i_property) )
error("karts");
setNumKarts(i_property);
// Difficulty
root->get("difficulty", &s_property);
setDifficulty(s_property);
// Time
root->get("time", &f_property); // one of time/position
setTime(f_property);
// Position
root->get("position", &m_position ); // must be set
if(m_time<0 && m_position<0)
error("position/time");
// Energy
root->get("energy", & i_property ); // This is optional
setEnergy(i_property);
// FIXME do we need position for the tutorials???????
// Position is optional except in GP and FTL
/*if(!root->get("position", &s_property))
error("position");
set
&&
//RaceManager::getWorld()->areKartsOrdered() ) // FIXME - order and optional are not the same thing
(m_minor==RaceManager::MINOR_MODE_FOLLOW_LEADER ||
m_major==RaceManager::MAJOR_MODE_GRAND_PRIX))
error("position");*/
if(m_major==RaceManager::MAJOR_MODE_SINGLE)
{
// Track
if (!root->get("track", &s_property ))
error("track");
setTrack(s_property);
if (!root->get("laps", &i_property))
error("laps");
setLaps(i_property);
}
else // GP
{
if (!root->get("gp", &m_gp_id )) error("gp");
if (grand_prix_manager->getGrandPrix(m_gp_id) == NULL) error("gp");
}
delete root;
} // TutorialData
// ----------------------------------------------------------------------------
void TutorialData::error(const char *id) const
{
std::ostringstream msg;
msg << "Undefined or incorrect value for '" << id
<< "' in tutorial file '" << m_filename << "'.";
std::cerr << "TutorialData : " << msg.str() << std::endl;
throw std::runtime_error(msg.str());
} // error
// ----------------------------------------------------------------------------
/** Checks if this challenge is valid, i.e. contains a valid track or a valid
* GP. If incorrect data are found, STK is aborted with an error message.
* (otherwise STK aborts when trying to do this challenge, which is worse).
*/
void TutorialData::check() const
{
if(m_major==RaceManager::MAJOR_MODE_SINGLE)
{
try
{
track_manager->getTrack(m_track_name);
}
catch(std::exception&)
{
error("track");
}
}
else if(m_major==RaceManager::MAJOR_MODE_GRAND_PRIX)
{
const GrandPrixData* gp = grand_prix_manager->getGrandPrix(m_gp_id);
if (gp == NULL)
{
error("gp");
}
const bool gp_ok = gp->checkConsistency(false);
if (!gp_ok)
{
error("gp");
}
}
} // check
void TutorialData::setRace() const
{
race_manager->setMajorMode(m_major);
if(m_major==RaceManager::MAJOR_MODE_SINGLE)
{
race_manager->setMinorMode(m_minor);
race_manager->setTrack(m_track_name);
race_manager->setDifficulty(m_difficulty);
race_manager->setNumLaps(m_num_laps);
race_manager->setNumKarts(m_num_karts);
race_manager->setNumPlayers(1);
race_manager->setNumLocalPlayers(1);
race_manager->setCoinTarget(m_energy);
}
else // GP
{
race_manager->setMinorMode(m_minor);
const GrandPrixData *gp = grand_prix_manager->getGrandPrix(m_gp_id);
race_manager->setGrandPrix(*gp);
race_manager->setDifficulty(m_difficulty);
race_manager->setNumKarts(m_num_karts);
race_manager->setNumPlayers(1);
race_manager->setNumLocalPlayers(1);
}
} // setRace
// ----------------------------------------------------------------------------
bool TutorialData::raceFinished()
{
// GP's use the grandPrixFinished() function, so they can't be fulfilled here.
if(m_major==RaceManager::MAJOR_MODE_GRAND_PRIX) return false;
// Single races
// ------------
World *world = World::getWorld();
std::string track_name = world->getTrack()->getIdent();
if(track_name!=m_track_name ) return false; // wrong track
if((int)world->getNumKarts()<m_num_karts ) return false; // not enough AI karts
Kart* kart = world->getPlayerKart(0);
if(m_energy>0 && kart->getEnergy() <m_energy ) return false; // not enough energy
if(m_position>0 && kart->getPosition()>m_position) return false; // too far behind
// Follow the leader
// -----------------
if(m_minor==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
// All possible conditions were already checked, so: must have been successful
return true;
}
// Quickrace / Timetrial
// ---------------------
// FIXME - encapsulate this better, each race mode needs to be able to specify
// its own challenges and deal with them
LinearWorld* lworld = dynamic_cast<LinearWorld*>(world);
if(lworld != NULL)
{
if(lworld->getLapForKart( kart->getWorldKartId() ) != m_num_laps) return false; // wrong number of laps
}
if(m_time>0.0f && kart->getFinishTime()>m_time) return false; // too slow
return true;
} // raceFinished
// ----------------------------------------------------------------------------
bool TutorialData::grandPrixFinished()
{
// Note that we have to call race_manager->getNumKarts, since there
// is no world objects to query at this stage.
if (race_manager->getMajorMode() != RaceManager::MAJOR_MODE_GRAND_PRIX ||
race_manager->getMinorMode() != m_minor ||
race_manager->getGrandPrix()->getId() != m_gp_id ||
race_manager->getDifficulty()!= m_difficulty ||
race_manager->getNumberOfKarts() < (unsigned int)m_num_karts ||
race_manager->getNumPlayers() > 1) return false;
// check if the player came first.
//assert(World::getWorld() != NULL);
// Kart* kart = World::getWorld()->getPlayerKart(0);
//const int rank = race_manager->getKartGPRank(kart->getWorldKartId());
const int rank = race_manager->getLocalPlayerGPRank(0);
//printf("getting rank for %s : %i \n", kart->getName().c_str(), rank );
if (rank != 0) return false;
return true;
} // grandPrixFinished
void TutorialData::setNumKarts(int num_karts)
{
this->m_num_karts= num_karts;
}
void TutorialData::setLaps(int num_laps)
{
this->m_num_laps = num_laps;
}
void TutorialData::setTrack(std::string track_name)
{
this->m_track_name == track_name;
if (track_manager->getTrack(m_track_name) == NULL)
error("track");
}
void TutorialData::setDifficulty(std::string difficulty)
{
if(difficulty == "easy")
this->m_difficulty = RaceManager::RD_EASY;
else if(difficulty =="medium")
this->m_difficulty = RaceManager::RD_MEDIUM;
else if(difficulty =="hard")
this->m_difficulty = RaceManager::RD_HARD;
else
error("difficulty");
}
void TutorialData::setMinor(std::string minor)
{
if(minor=="timetrial")
this->m_minor = RaceManager::MINOR_MODE_TIME_TRIAL;
else if(minor=="quickrace")
this->m_minor = RaceManager::MINOR_MODE_NORMAL_RACE;
else if(minor=="followtheleader")
this->m_minor = RaceManager::MINOR_MODE_FOLLOW_LEADER;
else
error("minor");
}
void TutorialData::setMajor(std::string major)
{
if(major=="grandprix")
this->m_major = RaceManager::MAJOR_MODE_GRAND_PRIX;
else if(major=="single")
this->m_major = RaceManager::MAJOR_MODE_SINGLE;
else
error("major");
}
void TutorialData::setPosition(int position)
{
this->m_position = position;
}
void TutorialData::setTime (float time)
{
this->m_time = time;
}
void TutorialData::setEnergy(int energy)
{
m_energy = energy;
}

@ -0,0 +1,76 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Alejandro Santiago
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_TUTORIAL_DATA_HPP
#define HEADER_TUTORIAL_DATA_HPP
#include <string>
#include <vector>
#include <stdio.h>
#include <stdexcept>
#include "tutorial/tutorial.hpp"
#include "race/race_manager.hpp"
/**
* \ingroup tutorial
*/
class TutorialData : public Tutorial
{
private:
RaceManager::MajorRaceModeType m_major;
RaceManager::MinorRaceModeType m_minor;
RaceManager::Difficulty m_difficulty;
int m_num_laps;
int m_position;
int m_num_karts;
float m_time;
std::string m_gp_id;
std::string m_track_name;
int m_energy;
std::string m_filename;
void getUnlocks(const XMLNode *root, const std:: string type, REWARD_TYPE reward);
void error(const char *id) const;
public:
#ifdef WIN32
TutorialData(const std::string& filename);
#else
TutorialData(const std::string& filename) throw(std::runtime_error);
#endif
/** sets the right parameters in RaceManager to try this tutorial */
void setRace() const;
void setEnergy(int m_energy);
void setTime(float m_time);
void setPosition(int position);
void setDifficulty(std::string difficulty);
void setTrack(std::string track_name);
void setLaps(int num_laps);
void setMajor(std::string major);
void setMinor(std::string minor);
void setNumKarts(int num_karts);
virtual void check() const;
virtual bool raceFinished();
virtual bool grandPrixFinished();
}; // TutorialData
#endif // HEADER_TUTORIAL_DATA_HPP