// SuperTuxKart - a fun racing game with go-kart // // Copyright (C) 2004-2015 Ingo Ruhnke // Copyright (C) 2006-2015 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "karts/kart_properties_manager.hpp" #include "challenges/unlock_manager.hpp" #include "config/player_manager.hpp" #include "config/player_profile.hpp" #include "config/stk_config.hpp" #include "config/user_config.hpp" #include "graphics/irr_driver.hpp" #include "guiengine/engine.hpp" #include "io/file_manager.hpp" #include "karts/kart_properties.hpp" #include "karts/xml_characteristic.hpp" #include "utils/log.hpp" #include "utils/string_utils.hpp" #include #include #include #include #include KartPropertiesManager *kart_properties_manager=0; std::vector KartPropertiesManager::m_kart_search_path; /** Constructor, only clears internal data structures. */ KartPropertiesManager::KartPropertiesManager() { m_all_groups.clear(); } // KartPropertiesManager //----------------------------------------------------------------------------- /** Destructor. Removes all allocated data. */ KartPropertiesManager::~KartPropertiesManager() { } // ~KartPropertiesManager //----------------------------------------------------------------------------- /** Adds a directory from which karts are loaded. The kart manager checks if * either this directory itself contains a kart, and if any subdirectory * contains a kart. * \param dir The directory to add. */ void KartPropertiesManager::addKartSearchDir(const std::string &s) { m_kart_search_path.push_back(s); } // addKartSearchDir //----------------------------------------------------------------------------- /** Removes all karts from the KartPropertiesManager, so that they can be * reloade. This is necessary after a change of the screen resolution. */ void KartPropertiesManager::unloadAllKarts() { m_karts_properties.clearAndDeleteAll(); m_selected_karts.clear(); m_kart_available.clear(); m_groups_2_indices.clear(); m_all_groups.clear(); } // unloadAllKarts //----------------------------------------------------------------------------- /** Remove a kart from the kart manager. * \param id The kart id (i.e. name of the directory) to remove. */ void KartPropertiesManager::removeKart(const std::string &ident) { // Remove the kart properties from the vector of all kart properties int index = getKartId(ident); const KartProperties *kp = getKart(ident); // must be done before remove m_karts_properties.remove(index); m_all_kart_dirs.erase(m_all_kart_dirs.begin()+index); m_kart_available.erase(m_kart_available.begin()+index); // Remove the just removed kart from the 'group-name to kart property // index' mapping. If a group is now empty (i.e. the removed kart was // the only member of this group), remove the group const std::vector &groups = kp->getGroups(); for (unsigned int i=0; i ::iterator it; it = std::find(m_groups_2_indices[groups[i]].begin(), m_groups_2_indices[groups[i]].end(), index); // Since we are iterating over all groups the kart belongs to, // there must be an entry found assert(it!=m_groups_2_indices[groups[i]].end()); m_groups_2_indices[groups[i]].erase(it); // Check if the last kart of a group was removed if(m_groups_2_indices[groups[i]].size()==0) { m_groups_2_indices.erase(groups[i]); std::vector::iterator its; its = std::find(m_all_groups.begin(), m_all_groups.end(), groups[i]); assert(its!=m_all_groups.end()); m_all_groups.erase(its); } // if m_groups_2_indices[groups[i]].size()==0) } // for i in all groups the kart belongs to // Adjust the indices of all kart properties in the 'group name to // kart property index' mapping: all kart properties with an index // greater than index were moved one position further to the beginning std::map >::iterator it_gr; for(it_gr=m_groups_2_indices.begin(); it_gr != m_groups_2_indices.end(); it_gr++) { for(unsigned int i=0; i<(*it_gr).second.size(); i++) { if( (*it_gr).second[i]>index) (*it_gr).second[i]--; } } delete kp; // Only used for networking and it is safe to just clear it. // If a networking game is started it will be initialised properly. m_selected_karts.clear(); } // removeKart //----------------------------------------------------------------------------- /** Loads all kart properties and models. */ void KartPropertiesManager::loadAllKarts(bool loading_icon) { m_all_kart_dirs.clear(); std::vector::const_iterator dir; for(dir = m_kart_search_path.begin(); dir!=m_kart_search_path.end(); dir++) { // First check if there is a kart in the current directory // ------------------------------------------------------- if(loadKart(*dir)) continue; // If not, check each subdir of this directory. // -------------------------------------------- std::set result; file_manager->listFiles(result, *dir); for(std::set::const_iterator subdir=result.begin(); subdir!=result.end(); subdir++) { const bool loaded = loadKart(*dir+*subdir); if (loaded && loading_icon) { GUIEngine::addLoadingIcon(irr_driver->getTexture( m_karts_properties[m_karts_properties.size()-1] .getAbsoluteIconFile() ) ); } } // for all files in the currently handled directory } // for i } // loadAllKarts //----------------------------------------------------------------------------- /** Loads the characteristics from the characteristics config file. * \param root The xml node where the characteristics are stored. */ void KartPropertiesManager::loadCharacteristics(const XMLNode *root) { // Load base characteristics std::vector nodes; root->getNodes("characteristic", nodes); bool found = false; std::string name; for (const XMLNode *baseNode : nodes) { baseNode->get("name", &name); if (name == "base") { found = true; m_base_characteristic.reset(new XmlCharacteristic(baseNode)); break; } } if (!found) Log::fatal("KartPropertiesManager", "Base characteristics not found"); // Load difficulties nodes.clear(); root->getNode("difficulties")->getNodes("characteristic", nodes); for (const XMLNode *type : nodes) { type->get("name", &name); m_difficulty_characteristics.insert(std::pair >(name, std::unique_ptr(new XmlCharacteristic(type)))); } // Load kart type characteristics nodes.clear(); root->getNode("kart-types")->getNodes("characteristic", nodes); for (const XMLNode *type : nodes) { type->get("name", &name); m_kart_type_characteristics.insert(std::pair >(name, std::unique_ptr(new XmlCharacteristic(type)))); } // Load player difficulties nodes.clear(); root->getNode("player-characteristics")->getNodes("characteristic", nodes); for (const XMLNode *type : nodes) { type->get("name", &name); m_player_characteristics.insert(std::pair >(name, std::unique_ptr(new XmlCharacteristic(type)))); } } //----------------------------------------------------------------------------- /** Loads a single kart and (if not disabled) the corresponding 3d model. * \param filename Full path to the kart config file. */ bool KartPropertiesManager::loadKart(const std::string &dir) { std::string config_filename = dir + "/kart.xml"; if(!file_manager->fileExists(config_filename)) return false; KartProperties* kart_properties; try { kart_properties = new KartProperties(config_filename); } catch (std::runtime_error& err) { Log::error("[KartPropertiesManager]", "Giving up loading '%s': %s", config_filename.c_str(), err.what()); return false; } // If the version of the kart file is not supported, // ignore this .kart file if (kart_properties->getVersion() < stk_config->m_min_kart_version || kart_properties->getVersion() > stk_config->m_max_kart_version) { Log::warn("[KartPropertiesManager]", "Warning: kart '%s' is not " "supported by this binary, ignored.", kart_properties->getIdent().c_str()); delete kart_properties; return false; } m_karts_properties.push_back(kart_properties); m_kart_available.push_back(true); const std::vector& groups=kart_properties->getGroups(); for(unsigned int g=0; g >::const_iterator it = m_difficulty_characteristics.find(type); if (it == m_difficulty_characteristics.cend()) return nullptr; return it->second.get(); } // getDifficultyCharacteristic //----------------------------------------------------------------------------- const AbstractCharacteristic* KartPropertiesManager::getKartTypeCharacteristic(const std::string &type) const { std::map >::const_iterator it = m_kart_type_characteristics.find(type); if (it == m_kart_type_characteristics.cend()) return nullptr; return it->second.get(); } // getKartTypeCharacteristic //----------------------------------------------------------------------------- const AbstractCharacteristic* KartPropertiesManager::getPlayerCharacteristic(const std::string &type) const { std::map >::const_iterator it = m_player_characteristics.find(type); if (it == m_player_characteristics.cend()) return nullptr; return it->second.get(); } // getPlayerCharacteristic //----------------------------------------------------------------------------- /** Returns index of the kart properties with the given ident. * \return Index of kart (between 0 and number of karts - 1). */ const int KartPropertiesManager::getKartId(const std::string &ident) const { for (unsigned int i=0; igetIdent() == ident) return kp; } return NULL; } // getKart //----------------------------------------------------------------------------- const KartProperties* KartPropertiesManager::getKartById(int i) const { if (i < 0 || i >= int(m_karts_properties.size())) return NULL; return m_karts_properties.get(i); } // getKartById //----------------------------------------------------------------------------- /** Returns a list of all available kart identifiers. */ std::vector KartPropertiesManager::getAllAvailableKarts() const { std::vector all; for (unsigned int i=0; i karts) { for (unsigned int i=0; i groups = m_karts_properties[i].getGroups(); if (std::find(groups.begin(), groups.end(), group) == groups.end()) continue; if (count == n) return i; count = count + 1; } return -1; } // getKartByGroup //----------------------------------------------------------------------------- bool KartPropertiesManager::testAndSetKart(int kartid) { if(!kartAvailable(kartid)) return false; m_selected_karts.push_back(kartid); return true; } // kartAvailable //----------------------------------------------------------------------------- /** Returns true if a kart is available to be selected. A kart is available to * be selected if it is available on all clients (i.e. m_kart_available is * true), not yet selected, and not locked. */ bool KartPropertiesManager::kartAvailable(int kartid) { if(kartid<0 || kartid>=(int)m_kart_available.size()) return false; if(!m_kart_available[kartid]) return false; std::vector::iterator it; for (it = m_selected_karts.begin(); it < m_selected_karts.end(); it++) { if ( kartid == *it) return false; } const KartProperties *kartprop = getKartById(kartid); if( PlayerManager::getCurrentPlayer()->isLocked(kartprop->getIdent()) ) return false; return true; } // kartAvailable //----------------------------------------------------------------------------- /** Sets a kart to be selected by specifying the identifier (name) of the kart. * \param kart_name Name of the kart. */ void KartPropertiesManager::selectKartName(const std::string &kart_name) { int kart_id = getKartId(kart_name); selectKart(kart_id); } // selectKartName //----------------------------------------------------------------------------- /** Returns a vector with the indices of all karts in the specified group. * \param g The name of the group for which the kart indicies should be * determined * \return A vector of indices with the karts in the given group. */ const std::vector KartPropertiesManager::getKartsInGroup( const std::string& g) { if (g == ALL_KART_GROUPS_ID) { std::vector out; for (unsigned int n=0; n *ai_list) { // First: set up flags (based on global kart // index) for which karts are already used // ----------------------------------------- std::vector used; used.resize(getNumberOfKarts(), false); std::vector random_kart_queue; if (existing_karts != NULL) { for (unsigned int i=0; isize(); i++) { try { int id = getKartId((*existing_karts)[i].getKartName()); used[id] = true; } catch (std::runtime_error& ex) { (void)ex; Log::error("[KartPropertiesManager]", "getRandomKartList : " "WARNING, can't find kart '%s'", (*existing_karts)[i].getKartName().c_str()); } } } for(unsigned int i=0; isize(); i++) { try { int id=getKartId((*ai_list)[i]); used[id] = true; } catch (std::runtime_error &ex) { (void)ex; Log::error("[KartPropertiesManager]", "getRandomKartList : WARNING, " "can't find kart '%s'",(*ai_list)[i].c_str()); } } do { // if we have no karts left in our queue, re-fill it if (count > 0 && random_kart_queue.size() == 0) { random_kart_queue.clear(); std::vector karts_in_group = getKartsInGroup(UserConfigParams::m_last_used_kart_group); assert(karts_in_group.size() > 0); // first try not to use a kart already used by a player for (unsigned int i=0; iisLocked(kp.getIdent()) ) { random_kart_queue.push_back(kp.getIdent()); } } // if we really need to, reuse the same kart as the player if (random_kart_queue.size() == 0) { for (unsigned int i=0; i 0); std::random_shuffle(random_kart_queue.begin(), random_kart_queue.end() ); } while (count > 0 && random_kart_queue.size() > 0) { ai_list->push_back(random_kart_queue.back()); random_kart_queue.pop_back(); count --; } } while (count > 0); // There should always be enough karts assert(count==0); } // getRandomKartList /* EOF */