// ============================================================== // This file is part of Glest (www.glest.org) // // Copyright (C) 2001-2008 MartiƱo Figueroa // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published // by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version // ============================================================== #include "tech_tree.h" #include #include "util.h" #include "resource.h" #include "faction_type.h" #include "logger.h" #include "xml_parser.h" #include "platform_util.h" #include "game_util.h" #include "window.h" #include "common_scoped_ptr.h" #include "leak_dumper.h" using namespace Shared::Util; using namespace Shared::Xml; namespace Glest{ namespace Game{ // ===================================================== // class TechTree // ===================================================== TechTree::TechTree(const vector pathList) { SkillType::resetNextAttackBoostId(); name=""; treePath=""; this->pathList.assign(pathList.begin(), pathList.end()); resourceTypes.clear(); factionTypes.clear(); armorTypes.clear(); attackTypes.clear(); translatedTechNames.clear(); translatedTechFactionNames.clear(); languageUsedForCache = ""; isValidationModeEnabled = false; } string TechTree::getNameUntranslated() const { return name; } string TechTree::getName(bool translatedValue) { if(translatedValue == false) { return getNameUntranslated(); } bool foundTranslation = false; Lang &lang = Lang::getInstance(); if(lang.getTechNameLoaded() != name || lang.getLanguage() != languageUsedForCache) { //printf("Line: %d Tech [%s]\n",__LINE__,name.c_str()); foundTranslation = lang.loadTechTreeStrings(name,lang.getLanguage() != languageUsedForCache); languageUsedForCache = lang.getLanguage(); translatedTechFactionNames.erase(name); translatedTechNames.erase(name); } string result = name; if(foundTranslation == true) { result = lang.getTechTreeString("TechTreeName",name.c_str()); } else { result = formatString(result); } //printf("Line: %d Tech [%s] result [%s]\n",__LINE__,name.c_str(),result.c_str()); return result; } string TechTree::getTranslatedName(string techName, bool forceLoad, bool forceTechtreeActiveFile) { string result = techName; //printf("Line: %d Tech [%s] forceLoad = %d forceTechtreeActiveFile = %d\n",__LINE__,techName.c_str(),forceLoad,forceTechtreeActiveFile); Lang &lang = Lang::getInstance(); if(forceTechtreeActiveFile == false && translatedTechNames.find(techName) != translatedTechNames.end() && lang.getLanguage() == languageUsedForCache) { result = translatedTechNames[techName]; } else { name = ""; string path = findPath(techName); if(path != "") { string currentPath = path; endPathWithSlash(currentPath); treePath = currentPath; name= lastDir(currentPath); lang.loadTechTreeStrings(name,lang.getLanguage() != languageUsedForCache); languageUsedForCache = lang.getLanguage(); translatedTechFactionNames.erase(techName); translatedTechNames.erase(techName); result = getName(true); //printf("techName [%s] name [%s] result [%s]\n",techName.c_str(),name.c_str(),result.c_str()); translatedTechNames[name] = result; } } return result; } string TechTree::getTranslatedFactionName(string techName, string factionName) { //printf("Line: %d Tech [%s] name [%s] factionName [%s]\n",__LINE__,techName.c_str(),name.c_str(),factionName.c_str()); Lang &lang = Lang::getInstance(); if(lang.getTechNameLoaded() != techName || lang.getLanguage() != languageUsedForCache) { //printf("Line: %d Tech [%s] name [%s] lang.getTechNameLoaded() [%s] factionName [%s]\n",__LINE__,techName.c_str(),name.c_str(),lang.getTechNameLoaded().c_str(),factionName.c_str()); lang.loadTechTreeStrings(techName,lang.getLanguage() != languageUsedForCache); languageUsedForCache = lang.getLanguage(); translatedTechFactionNames.erase(techName); } std::map >::iterator iterMap = translatedTechFactionNames.find(techName); if(iterMap != translatedTechFactionNames.end()) { if(iterMap->second.find(factionName) != iterMap->second.end()) { //printf("Line: %d Tech [%s] factionName [%s]\n",__LINE__,techName.c_str(),factionName.c_str()); return iterMap->second.find(factionName)->second; } } //printf("Line: %d Tech [%s] factionName [%s]\n",__LINE__,techName.c_str(),factionName.c_str()); getTranslatedName(techName,false,true); string result = lang.getTechTreeString("FactionName_" + factionName,formatString(factionName).c_str()); //printf(">>result = %s\n",result.c_str()); translatedTechFactionNames[techName][factionName] = result; //printf("Line: %d Translated faction for Tech [%s] faction [%s] result [%s]\n",__LINE__,techName.c_str(),factionName.c_str(),result.c_str()); return result; } Checksum TechTree::loadTech(const string &techName, set &factions, Checksum* checksum, std::map > > &loadedFileList, bool validationMode) { name = ""; isValidationModeEnabled = validationMode; Checksum techtreeChecksum; string path=findPath(techName); if(path!="") { //printf(">>> path=%s\n",path.c_str()); load(path, factions, checksum, &techtreeChecksum, loadedFileList, validationMode); } else { printf(">>> techtree [%s] path not found.\n",techName.c_str()); } return techtreeChecksum; } bool TechTree::exists(const string &techName, const vector &pathTechList) { bool techFound = false; auto_ptr techTree(new TechTree(pathTechList)); string path = techTree->findPath(techName); if(path != "") { techFound = true; } return techFound; } string TechTree::findPath(const string &techName) const { return findPath(techName,pathList); } string TechTree::findPath(const string &techName, const vector &pathTechList) { for(unsigned int idx = 0; idx < pathTechList.size(); ++idx) { string currentPath = (pathTechList)[idx]; endPathWithSlash(currentPath); string path = currentPath + techName; //printf(">>> test path=%s\n",path.c_str()); if(isdir(path.c_str()) == true) { return path; //break; } } //return "no path found for tech: \""+techname+"\""; return ""; } void TechTree::load(const string &dir, set &factions, Checksum* checksum, Checksum *techtreeChecksum, std::map > > &loadedFileList, bool validationMode) { if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); string currentPath = dir; endPathWithSlash(currentPath); treePath = currentPath; name= lastDir(currentPath); Lang &lang = Lang::getInstance(); lang.loadTechTreeStrings(name, true); languageUsedForCache = lang.getLanguage(); char szBuf[8096]=""; snprintf(szBuf,8096,Lang::getInstance().getString("LogScreenGameLoadingTechtree","",true).c_str(),formatString(getName(true)).c_str()); Logger::getInstance().add(szBuf, true); vector filenames; //load resources string str= currentPath + "resources/*."; try { findAll(str, filenames); resourceTypes.resize(filenames.size()); for(int i = 0; i < (int)filenames.size(); ++i){ str= currentPath + "resources/" + filenames[i]; resourceTypes[i].load(str, checksum, &checksumValue, loadedFileList, treePath); Window::handleEvent(); SDL_PumpEvents(); } // Cleanup pixmap memory for(int i = 0; i < (int)filenames.size(); ++i) { resourceTypes[i].deletePixels(); } } catch(megaglest_runtime_error& ex) { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what()); throw megaglest_runtime_error("Error loading Resource Types in: "+ currentPath + "\nMessage: " + ex.what(),!ex.wantStackTrace() || isValidationModeEnabled); } catch(const exception &e){ SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what()); throw megaglest_runtime_error("Error loading Resource Types in: "+ currentPath + "\nMessage: " + e.what(),isValidationModeEnabled); } // give CPU time to update other things to avoid apperance of hanging sleep(0); Window::handleEvent(); SDL_PumpEvents(); //load tech tree xml info try { XmlTree xmlTree; string currentPath = dir; endPathWithSlash(currentPath); string path = currentPath + lastDir(dir) + ".xml"; checksum->addFile(path); checksumValue.addFile(path); std::map mapExtraTagReplacementValues; mapExtraTagReplacementValues["$COMMONDATAPATH"] = currentPath + "/commondata/"; xmlTree.load(path, Properties::getTagReplacementValues(&mapExtraTagReplacementValues)); loadedFileList[path].push_back(make_pair(currentPath,currentPath)); Properties::setTechtreePath(currentPath); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("==> Set techtree path to [%s]\n",currentPath.c_str()); const XmlNode *techTreeNode= xmlTree.getRootNode(); //attack types const XmlNode *attackTypesNode= techTreeNode->getChild("attack-types"); attackTypes.resize(attackTypesNode->getChildCount()); for(int i = 0; i < (int)attackTypes.size(); ++i) { const XmlNode *attackTypeNode= attackTypesNode->getChild("attack-type", i); attackTypes[i].setName(attackTypeNode->getAttribute("name")->getRestrictedValue()); attackTypes[i].setId(i); Window::handleEvent(); SDL_PumpEvents(); } // give CPU time to update other things to avoid apperance of hanging sleep(0); //SDL_PumpEvents(); //armor types const XmlNode *armorTypesNode= techTreeNode->getChild("armor-types"); armorTypes.resize(armorTypesNode->getChildCount()); for(int i = 0; i < (int)armorTypes.size(); ++i) { const XmlNode *armorTypeNode= armorTypesNode->getChild("armor-type", i); armorTypes[i].setName(armorTypeNode->getAttribute("name")->getRestrictedValue()); armorTypes[i].setId(i); Window::handleEvent(); SDL_PumpEvents(); } //damage multipliers damageMultiplierTable.init((int)attackTypes.size(), (int)armorTypes.size()); const XmlNode *damageMultipliersNode= techTreeNode->getChild("damage-multipliers"); for(int i = 0; i < (int)damageMultipliersNode->getChildCount(); ++i) { const XmlNode *damageMultiplierNode= damageMultipliersNode->getChild("damage-multiplier", i); const AttackType *attackType= getAttackType(damageMultiplierNode->getAttribute("attack")->getRestrictedValue()); const ArmorType *armorType= getArmorType(damageMultiplierNode->getAttribute("armor")->getRestrictedValue()); double multiplier= damageMultiplierNode->getAttribute("value")->getFloatValue(); damageMultiplierTable.setDamageMultiplier(attackType, armorType, multiplier); Window::handleEvent(); SDL_PumpEvents(); } } catch(megaglest_runtime_error& ex) { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what()); throw megaglest_runtime_error("Error loading Tech Tree: "+ currentPath + "\nMessage: " + ex.what(),!ex.wantStackTrace() || isValidationModeEnabled); } catch(const exception &e){ SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what()); throw megaglest_runtime_error("Error loading Tech Tree: "+ currentPath + "\nMessage: " + e.what(),isValidationModeEnabled); } // give CPU time to update other things to avoid apperance of hanging sleep(0); //SDL_PumpEvents(); //load factions try{ factionTypes.resize(factions.size()); int i=0; for ( set::iterator it = factions.begin(); it != factions.end(); ++it ) { string factionName = *it; char szBuf[8096]=""; snprintf(szBuf,8096,"%s %s [%d / %d] - %s",Lang::getInstance().getString("Loading").c_str(), Lang::getInstance().getString("Faction").c_str(), i+1, (int)factions.size(), formatString(this->getTranslatedFactionName(name,factionName)).c_str()); Logger &logger= Logger::getInstance(); logger.setState(szBuf); logger.setProgress((int)((((double)i) / (double)factions.size()) * 100.0)); factionTypes[i++].load(factionName, this, checksum,&checksumValue, loadedFileList,validationMode); // give CPU time to update other things to avoid apperance of hanging sleep(0); Window::handleEvent(); SDL_PumpEvents(); } } catch(megaglest_runtime_error& ex) { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what()); throw megaglest_runtime_error("Error loading Faction Types: "+ currentPath + "\nMessage: " + ex.what(),!ex.wantStackTrace() || isValidationModeEnabled); } catch(const exception &e){ SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,e.what()); throw megaglest_runtime_error("Error loading Faction Types: "+ currentPath + "\nMessage: " + e.what(),isValidationModeEnabled); } if(techtreeChecksum != NULL) { *techtreeChecksum = checksumValue; } if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); } TechTree::~TechTree() { if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); Logger::getInstance().add(Lang::getInstance().getString("LogScreenGameUnLoadingTechtree","",true), true); resourceTypes.clear(); factionTypes.clear(); armorTypes.clear(); attackTypes.clear(); } std::vector TechTree::validateFactionTypes() { std::vector results; for (int i = 0; i < (int)factionTypes.size(); ++i) { std::vector factionResults = factionTypes[i].validateFactionType(); if(factionResults.empty() == false) { results.insert(results.end(), factionResults.begin(), factionResults.end()); } factionResults = factionTypes[i].validateFactionTypeUpgradeTypes(); if(factionResults.empty() == false) { results.insert(results.end(), factionResults.begin(), factionResults.end()); } } return results; } std::vector TechTree::validateResourceTypes() { std::vector results; ResourceTypes resourceTypesNotUsed = resourceTypes; for (unsigned int i = 0; i < resourceTypesNotUsed.size(); ++i) { ResourceType &rt = resourceTypesNotUsed[i]; rt.setCleanupMemory(false); } for (unsigned int i = 0; i < factionTypes.size(); ++i) { //printf("Validating [%d / %d] faction [%s]\n",i,(int)factionTypes.size(),factionTypes[i].getName().c_str()); std::vector factionResults = factionTypes[i].validateFactionTypeResourceTypes(resourceTypes); if(factionResults.empty() == false) { results.insert(results.end(), factionResults.begin(), factionResults.end()); } // Check if the faction uses the resources in this techtree // Now lets find a matching faction resource type for the unit for(int j = (int)resourceTypesNotUsed.size() -1; j >= 0; --j) { const ResourceType &rt = resourceTypesNotUsed[j]; //printf("Validating [%d / %d] resourcetype [%s]\n",j,(int)resourceTypesNotUsed.size(),rt.getName().c_str()); if(factionTypes[i].factionUsesResourceType(&rt) == true) { //printf("FOUND FACTION CONSUMER FOR RESOURCE - [%d / %d] faction [%s] resource [%d / %d] resourcetype [%s]\n",i,(int)factionTypes.size(),factionTypes[i].getName().c_str(),j,(int)resourceTypesNotUsed.size(),rt.getName().c_str()); resourceTypesNotUsed.erase(resourceTypesNotUsed.begin() + j); } } } if(resourceTypesNotUsed.empty() == false) { //printf("FOUND unused resource Types [%d]\n",(int)resourceTypesNotUsed.size()); for (unsigned int i = 0; i < resourceTypesNotUsed.size(); ++i) { const ResourceType &rt = resourceTypesNotUsed[i]; char szBuf[8096]=""; snprintf(szBuf,8096,"The Resource type [%s] is not used by any units in this techtree!",rt.getName().c_str()); results.push_back(szBuf); } } return results; } // ==================== get ==================== FactionType *TechTree::getTypeByName(const string &name) { for(int i=0; i < (int)factionTypes.size(); ++i) { if(factionTypes[i].getName(false) == name) { return &factionTypes[i]; } } if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); throw megaglest_runtime_error("Faction not found: " + name,true); } const FactionType *TechTree::getType(const string &name) const { for(int i=0; i < (int)factionTypes.size(); ++i) { if(factionTypes[i].getName(false) == name) { return &factionTypes[i]; } } if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); throw megaglest_runtime_error("Faction not found: " + name,true); } const ResourceType *TechTree::getTechResourceType(int i) const{ for(int j=0; j < getResourceTypeCount(); ++j){ const ResourceType *rt= getResourceType(j); assert(rt != NULL); if(rt == NULL) { throw megaglest_runtime_error("rt == NULL"); } if(rt->getResourceNumber() == i && rt->getClass() == rcTech) return getResourceType(j); } return getFirstTechResourceType(); } const ResourceType *TechTree::getFirstTechResourceType() const{ for(int i=0; igetResourceNumber()==1 && rt->getClass()==rcTech) return getResourceType(i); } char szBuf[8096]=""; snprintf(szBuf,8096,"The referenced tech tree [%s] is either missing or has no resources defined but at least one resource is required.",this->name.c_str()); throw megaglest_runtime_error(szBuf,true); } const ResourceType *TechTree::getResourceType(const string &name) const{ for(int i=0; i < (int)resourceTypes.size(); ++i){ if(resourceTypes[i].getName()==name){ return &resourceTypes[i]; } } throw megaglest_runtime_error("Resource Type not found: " + name,true); } const ArmorType *TechTree::getArmorType(const string &name) const{ for(int i=0; i < (int)armorTypes.size(); ++i){ if(armorTypes[i].getName(false)==name){ return &armorTypes[i]; } } throw megaglest_runtime_error("Armor Type not found: " + name,true); } const AttackType *TechTree::getAttackType(const string &name) const{ for(int i=0; i < (int)attackTypes.size(); ++i){ if(attackTypes[i].getName(false)==name){ return &attackTypes[i]; } } throw megaglest_runtime_error("Attack Type not found: " + name,true); } double TechTree::getDamageMultiplier(const AttackType *att, const ArmorType *art) const { return damageMultiplierTable.getDamageMultiplier(att, art); } void TechTree::saveGame(XmlNode *rootNode) { std::map mapTagReplacements; XmlNode *techTreeNode = rootNode->addChild("TechTree"); // string name; techTreeNode->addAttribute("name",name, mapTagReplacements); // //string desc; // string treePath; //techTreeNode->addAttribute("treePath",treePath, mapTagReplacements); // vector pathList; // for(unsigned int i = 0; i < pathList.size(); ++i) { // XmlNode *pathListNode = techTreeNode->addChild("pathList"); // pathListNode->addAttribute("value",pathList[i], mapTagReplacements); // } // ResourceTypes resourceTypes; // for(unsigned int i = 0; i < resourceTypes.size(); ++i) { // ResourceType &rt = resourceTypes[i]; // rt.saveGame(techTreeNode); // } // FactionTypes factionTypes; // for(unsigned int i = 0; i < factionTypes.size(); ++i) { // FactionType &ft = factionTypes[i]; // ft.saveGame(techTreeNode); // } // ArmorTypes armorTypes; // for(unsigned int i = 0; i < armorTypes.size(); ++i) { // ArmorType &at = armorTypes[i]; // at.saveGame(techTreeNode); // } // AttackTypes attackTypes; // for(unsigned int i = 0; i < attackTypes.size(); ++i) { // AttackType &at = attackTypes[i]; // at.saveGame(techTreeNode); // } // DamageMultiplierTable damageMultiplierTable; // damageMultiplierTable.saveGame(techTreeNode); // Checksum checksumValue; techTreeNode->addAttribute("checksumValue",intToStr(checksumValue.getSum()), mapTagReplacements); } }}//end namespace