diff --git a/src/challenges/challenge_data.cpp b/src/challenges/challenge_data.cpp index a380e7abb..bc3fa8742 100644 --- a/src/challenges/challenge_data.cpp +++ b/src/challenges/challenge_data.cpp @@ -21,6 +21,7 @@ #include #include "challenges/unlock_manager.hpp" +#include "io/file_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" @@ -28,6 +29,7 @@ #include "race/grand_prix_data.hpp" #include "race/grand_prix_manager.hpp" #include "race/race_manager.hpp" +#include "replay/replay_play.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" @@ -41,6 +43,7 @@ ChallengeData::ChallengeData(const std::string& filename) m_gp_id = ""; m_version = 0; m_num_trophies = 0; + m_is_ghost_replay = false; for (int d=0; dget("number", &num_karts)) error("karts"); m_num_karts[d] = num_karts; + std::string replay_file; + if (karts_node->get("replay_file", &replay_file)) + { + m_is_ghost_replay = true; + m_replay_files[d] = replay_file; + } + std::string ai_kart_ident; if (karts_node->get("aiIdent", &ai_kart_ident)) m_ai_kart_ident[d] = ai_kart_ident; @@ -387,6 +397,16 @@ void ChallengeData::setRace(RaceManager::Difficulty d) const race_manager->setNumPlayers(1); } + if (m_is_ghost_replay) + { + const bool result = ReplayPlay::get()->addReplayFile(file_manager + ->getAsset(FileManager::CHALLENGE, m_replay_files[d]), + true/*custom_replay*/); + if (!result) + Log::fatal("ChallengeData", "Can't open replay for challenge!"); + race_manager->setRaceGhostKarts(true); + } + if (m_ai_kart_ident[d] != "") { race_manager->setAIKartOverride(m_ai_kart_ident[d]); diff --git a/src/challenges/challenge_data.hpp b/src/challenges/challenge_data.hpp index 5c9889f1f..f563695fb 100644 --- a/src/challenges/challenge_data.hpp +++ b/src/challenges/challenge_data.hpp @@ -86,6 +86,7 @@ private: int m_position[RaceManager::DIFFICULTY_COUNT]; int m_num_karts[RaceManager::DIFFICULTY_COUNT]; std::string m_ai_kart_ident[RaceManager::DIFFICULTY_COUNT]; + std::string m_replay_files[RaceManager::DIFFICULTY_COUNT]; float m_time[RaceManager::DIFFICULTY_COUNT]; int m_energy[RaceManager::DIFFICULTY_COUNT]; RaceManager::AISuperPower m_ai_superpower[RaceManager::DIFFICULTY_COUNT]; @@ -94,6 +95,7 @@ private: std::string m_filename; /** Version number of the challenge. */ int m_version; + bool m_is_ghost_replay; void setUnlocks(const std::string &id, ChallengeData::RewardType reward); @@ -182,6 +184,9 @@ public: /** Returns if this challenge is a grand prix. */ bool isSingleRace() const { return m_mode == CM_SINGLE_RACE; } // ------------------------------------------------------------------------ + /** Returns if this challenge is using ghost replay. */ + bool isGhostReplay() const { return m_is_ghost_replay; } + // ------------------------------------------------------------------------ /** Returns the challenge mode of this challenge. */ ChallengeModeType getMode() const { return m_mode; } // ------------------------------------------------------------------------ diff --git a/src/replay/replay_base.cpp b/src/replay/replay_base.cpp index bcf450c5f..912d5766c 100644 --- a/src/replay/replay_base.cpp +++ b/src/replay/replay_base.cpp @@ -26,12 +26,14 @@ ReplayBase::ReplayBase() // ----------------------------------------------------------------------------- /** Opens a replay file which is determined by sub classes. * \param writeable True if the file should be opened for writing. + * \param full_path True if the file is full path. * \return A FILE *, or NULL if the file could not be opened. */ -FILE* ReplayBase::openReplayFile(bool writeable) +FILE* ReplayBase::openReplayFile(bool writeable, bool full_path) { - FILE *fd = fopen((file_manager->getReplayDir() + - getReplayFilename()).c_str(), writeable ? "w" : "r"); + FILE *fd = fopen(full_path ? getReplayFilename().c_str() : + (file_manager->getReplayDir() + getReplayFilename()).c_str(), + writeable ? "w" : "r"); if (!fd) { return NULL; diff --git a/src/replay/replay_base.hpp b/src/replay/replay_base.hpp index fac18a16a..08e846b5a 100644 --- a/src/replay/replay_base.hpp +++ b/src/replay/replay_base.hpp @@ -68,7 +68,7 @@ protected: }; // KartReplayEvent // ------------------------------------------------------------------------ - FILE *openReplayFile(bool writeable); + FILE *openReplayFile(bool writeable, bool full_path = false); // ------------------------------------------------------------------------ /** Returns the filename that was opened. */ virtual const std::string& getReplayFilename() const = 0; diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 467481700..f74323811 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -40,6 +40,7 @@ ReplayPlay *ReplayPlay::m_replay_play = NULL; ReplayPlay::ReplayPlay() { m_current_replay_file = 0; + m_custom_replay_file = false; } // ReplayPlay //----------------------------------------------------------------------------- @@ -67,113 +68,135 @@ void ReplayPlay::loadAllReplayFile() file_manager->listFiles(files, file_manager->getReplayDir(), /*is_full_path*/ false); - char s[1024], s1[1024]; for (std::set::iterator i = files.begin(); i != files.end(); ++i) { - if (StringUtils::getExtension(*i) != "replay") continue; - FILE *fd = fopen((file_manager->getReplayDir() + (*i)).c_str(), "r"); - if (fd == NULL) continue; - ReplayData rd; - - rd.m_filename = *i; - - fgets(s, 1023, fd); - unsigned int version; - if (sscanf(s,"version: %u", &version) != 1) + if (!addReplayFile(*i)) { - Log::warn("Replay", "No Version information " - "found in replay file (bogus replay file)."); - fclose(fd); + // Skip invalid replay file continue; } - if (version != getReplayVersion()) - { - Log::warn("Replay", "Replay is version '%d'", version); - Log::warn("Replay", "STK version is '%d'", getReplayVersion()); - Log::warn("Replay", "Skipped '%s'", i->c_str()); - fclose(fd); - continue; - } - - while(true) - { - fgets(s, 1023, fd); - core::stringc is_end(s); - is_end.trim(); - if (is_end == "kart_list_end") break; - char s1[1024]; - - if (sscanf(s,"kart: %s", s1) != 1) - { - Log::warn("Replay", "Could not read ghost karts info!"); - break; - } - rd.m_kart_list.push_back(std::string(s1)); - } - - int reverse = 0; - fgets(s, 1023, fd); - if(sscanf(s, "reverse: %d", &reverse) != 1) - { - Log::warn("Replay", "Reverse info found in replay file."); - fclose(fd); - continue; - } - rd.m_reverse = reverse!=0; - - fgets(s, 1023, fd); - if (sscanf(s, "difficulty: %u", &rd.m_difficulty) != 1) - { - Log::warn("Replay", " No difficulty found in replay file."); - fclose(fd); - continue; - } - - fgets(s, 1023, fd); - if (sscanf(s, "track: %s", s1) != 1) - { - Log::warn("Replay", "Track info not found in replay file."); - fclose(fd); - continue; - } - rd.m_track_name = std::string(s1); - Track* t = track_manager->getTrack(rd.m_track_name); - if (t == NULL) - { - Log::warn("Replay", "Track '%s' used in replay not found in STK!", - rd.m_track_name.c_str()); - fclose(fd); - continue; - } - - fgets(s, 1023, fd); - if (sscanf(s, "laps: %u", &rd.m_laps) != 1) - { - Log::warn("Replay", "No number of laps found in replay file."); - fclose(fd); - continue; - } - - fgets(s, 1023, fd); - if (sscanf(s, "min_time: %f", &rd.m_min_time) != 1) - { - Log::warn("Replay", "Finish time not found in replay file."); - fclose(fd); - continue; - } - fclose(fd); - m_replay_file_list.push_back(rd); } } // loadAllReplayFile +//----------------------------------------------------------------------------- +bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay) +{ + // custom_replay is true when full path of filename is given + m_custom_replay_file = custom_replay; + + char s[1024], s1[1024]; + if (StringUtils::getExtension(fn) != "replay") return false; + FILE *fd = fopen(custom_replay ? fn.c_str() : + (file_manager->getReplayDir() + fn).c_str(), "r"); + if (fd == NULL) return false; + ReplayData rd; + + rd.m_filename = fn; + + fgets(s, 1023, fd); + unsigned int version; + if (sscanf(s,"version: %u", &version) != 1) + { + Log::warn("Replay", "No Version information " + "found in replay file (bogus replay file)."); + fclose(fd); + return false; + } + if (version != getReplayVersion()) + { + Log::warn("Replay", "Replay is version '%d'", version); + Log::warn("Replay", "STK version is '%d'", getReplayVersion()); + Log::warn("Replay", "Skipped '%s'", fn.c_str()); + fclose(fd); + return false; + } + + while(true) + { + fgets(s, 1023, fd); + core::stringc is_end(s); + is_end.trim(); + if (is_end == "kart_list_end") break; + char s1[1024]; + + if (sscanf(s,"kart: %s", s1) != 1) + { + Log::warn("Replay", "Could not read ghost karts info!"); + break; + } + rd.m_kart_list.push_back(std::string(s1)); + } + + int reverse = 0; + fgets(s, 1023, fd); + if(sscanf(s, "reverse: %d", &reverse) != 1) + { + Log::warn("Replay", "Reverse info found in replay file."); + fclose(fd); + return false; + } + rd.m_reverse = reverse != 0; + + fgets(s, 1023, fd); + if (sscanf(s, "difficulty: %u", &rd.m_difficulty) != 1) + { + Log::warn("Replay", " No difficulty found in replay file."); + fclose(fd); + return false; + } + + fgets(s, 1023, fd); + if (sscanf(s, "track: %s", s1) != 1) + { + Log::warn("Replay", "Track info not found in replay file."); + fclose(fd); + return false; + } + rd.m_track_name = std::string(s1); + Track* t = track_manager->getTrack(rd.m_track_name); + if (t == NULL) + { + Log::warn("Replay", "Track '%s' used in replay not found in STK!", + rd.m_track_name.c_str()); + fclose(fd); + return false; + } + + fgets(s, 1023, fd); + if (sscanf(s, "laps: %u", &rd.m_laps) != 1) + { + Log::warn("Replay", "No number of laps found in replay file."); + fclose(fd); + return false; + } + + fgets(s, 1023, fd); + if (sscanf(s, "min_time: %f", &rd.m_min_time) != 1) + { + Log::warn("Replay", "Finish time not found in replay file."); + fclose(fd); + return false; + } + fclose(fd); + m_replay_file_list.push_back(rd); + + assert(m_replay_file_list.size() > 0); + // Force to use custom replay file immediately + if (custom_replay) + m_current_replay_file = m_replay_file_list.size() - 1; + + return true; + +} // addReplayFile + //----------------------------------------------------------------------------- void ReplayPlay::load() { m_ghost_karts.clearAndDeleteAll(); char s[1024]; - FILE *fd = openReplayFile(/*writeable*/false); + FILE *fd = openReplayFile(/*writeable*/false, m_custom_replay_file); if(!fd) { Log::error("Replay", "Can't read '%s', ghost replay disabled.", diff --git a/src/replay/replay_play.hpp b/src/replay/replay_play.hpp index f365b2b81..65fd10c97 100644 --- a/src/replay/replay_play.hpp +++ b/src/replay/replay_play.hpp @@ -93,6 +93,8 @@ private: unsigned int m_current_replay_file; + bool m_custom_replay_file; + std::vector m_replay_file_list; /** All ghost karts. */ @@ -118,6 +120,9 @@ public: void setReplayFile(unsigned int n) { m_current_replay_file = n; } // ------------------------------------------------------------------------ + bool addReplayFile(const std::string& fn, + bool custom_replay = false); + // ------------------------------------------------------------------------ const ReplayData& getReplayData(unsigned int n) const { return m_replay_file_list.at(n); } // ------------------------------------------------------------------------ diff --git a/src/states_screens/dialogs/select_challenge.cpp b/src/states_screens/dialogs/select_challenge.cpp index 139195aac..857adac4a 100644 --- a/src/states_screens/dialogs/select_challenge.cpp +++ b/src/states_screens/dialogs/select_challenge.cpp @@ -63,8 +63,11 @@ core::stringw getLabel(RaceManager::Difficulty difficulty, const ChallengeData* label.append( _("Required Nitro Points: %i", c->getEnergy(difficulty)) ); } - if (label.size() > 0) label.append(L"\n"); - label.append(_("Number of AI Karts: %i", c->getNumKarts(difficulty) - 1)); + if (!c->isGhostReplay()) + { + if (label.size() > 0) label.append(L"\n"); + label.append(_("Number of AI Karts: %i", c->getNumKarts(difficulty) - 1)); + } return label; } @@ -144,6 +147,8 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth, typeLbl->setText(_("Grand Prix"), false ); else if (c->getData()->getEnergy(RaceManager::DIFFICULTY_EASY) > 0) typeLbl->setText(_("Nitro challenge"), false ); + else if (c->getData()->isGhostReplay()) + typeLbl->setText(_("Ghost replay race"), false ); else typeLbl->setText( RaceManager::getNameOf(c->getData()->getMinorMode()), false );