// ============================================================== // 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 // ============================================================== #ifdef WIN32 #include #include #endif #include "unit_type.h" #include #include "util.h" #include "upgrade_type.h" #include "resource_type.h" #include "sound.h" #include "logger.h" #include "xml_parser.h" #include "tech_tree.h" #include "resource.h" #include "renderer.h" #include "game_util.h" #include "unit_particle_type.h" #include "faction.h" #include "common_scoped_ptr.h" #include "leak_dumper.h" using namespace Shared::Xml; using namespace Shared::Graphics; using namespace Shared::Util; namespace Glest{ namespace Game{ auto_ptr UnitType::ctHarvestEmergencyReturnCommandType(new HarvestEmergencyReturnCommandType()); // =============================== // class Level // =============================== void Level::init(string name, int kills){ this->name= name; this->kills= kills; } string Level::getName(bool translatedValue) const { if(translatedValue == false) return name; Lang &lang = Lang::getInstance(); return lang.getTechTreeString("LevelName_" + name,name.c_str()); } void Level::saveGame(XmlNode *rootNode) const { std::map mapTagReplacements; XmlNode *levelNode = rootNode->addChild("Level"); levelNode->addAttribute("name",name, mapTagReplacements); levelNode->addAttribute("kills",intToStr(kills), mapTagReplacements); } const Level * Level::loadGame(const XmlNode *rootNode,const UnitType *ut) { const Level *result = NULL; if(rootNode->hasChild("Level") == true) { const XmlNode *levelNode = rootNode->getChild("Level"); result = ut->getLevel(levelNode->getAttribute("name")->getValue()); } return result; } // ===================================================== // class UnitType // ===================================================== // ===================== PUBLIC ======================== const char *UnitType::propertyNames[]= {"burnable", "rotated_climb"}; // ==================== creation and loading ==================== UnitType::UnitType() : ProducibleType() { countInVictoryConditions = ucvcNotSet; meetingPointImage = NULL; lightColor= Vec3f(0.f); light= false; healthbarheight= -100.0f; healthbarthickness=-1.0f; healthbarVisible=hbvUndefined; multiSelect= false; uniformSelect= false; commandable= true; armorType= NULL; rotatedBuildPos=0; field = fLand; id = 0; meetingPoint = false; rotationAllowed = false; countUnitDeathInStats=false; countUnitProductionInStats=false; countUnitKillInStats=false; countKillForUnitUpgrade=false; for(int i=0; imaxHp),this->maxHp); addItemToVault(&(this->hpRegeneration),this->hpRegeneration); addItemToVault(&(this->maxEp),this->maxEp); addItemToVault(&(this->epRegeneration),this->epRegeneration); addItemToVault(&(this->maxUnitCount),this->maxUnitCount); addItemToVault(&(this->armor),this->armor); addItemToVault(&(this->sight),this->sight); addItemToVault(&(this->size),this->size); addItemToVault(&(this->height),this->height); } UnitType::~UnitType(){ deleteValues(commandTypes.begin(), commandTypes.end()); commandTypes.clear(); deleteValues(skillTypes.begin(), skillTypes.end()); skillTypes.clear(); deleteValues(selectionSounds.getSounds().begin(), selectionSounds.getSounds().end()); selectionSounds.clearSounds(); deleteValues(commandSounds.getSounds().begin(), commandSounds.getSounds().end()); commandSounds.clearSounds(); delete [] cellMap; cellMap=NULL; //remove damageParticleSystemTypes while(!damageParticleSystemTypes.empty()){ delete damageParticleSystemTypes.back(); damageParticleSystemTypes.pop_back(); } } void UnitType::preLoad(const string &dir) { name= lastDir(dir); } void UnitType::loaddd(int id,const string &dir, const TechTree *techTree, const string &techTreePath, const FactionType *factionType, 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); string path = currentPath + name + ".xml"; string sourceXMLFile = path; this->id= id; try { //Lang &lang= Lang::getInstance(); char szBuf[8096]=""; snprintf(szBuf,8096,Lang::getInstance().getString("LogScreenGameLoadingUnitType","",true).c_str(),formatString(this->getName(true)).c_str()); Logger::getInstance().add(szBuf, true); //file load checksum->addFile(path); techtreeChecksum->addFile(path); XmlTree xmlTree; std::map mapExtraTagReplacementValues; mapExtraTagReplacementValues["$COMMONDATAPATH"] = techTreePath + "/commondata/"; xmlTree.load(path, Properties::getTagReplacementValues(&mapExtraTagReplacementValues)); loadedFileList[path].push_back(make_pair(dir,dir)); const XmlNode *unitNode= xmlTree.getRootNode(); const XmlNode *parametersNode= unitNode->getChild("parameters"); if(parametersNode->hasChild("count-in-victory-conditions") == true) { bool countUnit = parametersNode->getChild("count-in-victory-conditions")->getAttribute("value")->getBoolValue(); if(countUnit == true) { countInVictoryConditions = ucvcTrue; } else { countInVictoryConditions = ucvcFalse; } } //size //checkItemInVault(&(this->size),this->size); size= parametersNode->getChild("size")->getAttribute("value")->getIntValue(); addItemToVault(&(this->size),this->size); renderSize=size; if(parametersNode->hasChild("render-size")){ renderSize=parametersNode->getChild("render-size")->getAttribute("value")->getIntValue(); } aiBuildSize=size; if(parametersNode->hasChild("ai-build-size")){ aiBuildSize= parametersNode->getChild("ai-build-size")->getAttribute("value")->getIntValue(); } //height //checkItemInVault(&(this->height),this->height); height= parametersNode->getChild("height")->getAttribute("value")->getIntValue(); addItemToVault(&(this->height),this->height); //targetHeight if(parametersNode->hasChild("target-height")){ targetHeight= parametersNode->getChild("target-height")->getAttribute("value")->getIntValue(); addItemToVault(&(this->targetHeight),this->targetHeight); } else { targetHeight=height; } //burnHeight if(parametersNode->hasChild("target-height")){ burnHeight= parametersNode->getChild("burn-height")->getAttribute("value")->getIntValue(); addItemToVault(&(this->burnHeight),this->burnHeight); } else { burnHeight=height; } //maxHp //checkItemInVault(&(this->maxHp),this->maxHp); maxHp = parametersNode->getChild("max-hp")->getAttribute("value")->getIntValue(); addItemToVault(&(this->maxHp),this->maxHp); //hpRegeneration //checkItemInVault(&(this->hpRegeneration),this->hpRegeneration); hpRegeneration= parametersNode->getChild("max-hp")->getAttribute("regeneration")->getIntValue(); addItemToVault(&(this->hpRegeneration),this->hpRegeneration); //maxEp //checkItemInVault(&(this->maxEp),this->maxEp); maxEp= parametersNode->getChild("max-ep")->getAttribute("value")->getIntValue(); addItemToVault(&(this->maxEp),this->maxEp); if(maxEp != 0) { //epRegeneration //checkItemInVault(&(this->epRegeneration),this->epRegeneration); epRegeneration= parametersNode->getChild("max-ep")->getAttribute("regeneration")->getIntValue(); } addItemToVault(&(this->epRegeneration),this->epRegeneration); // Check that we don't use both start-value and start-percentage, as they are mutually // exclusive if(parametersNode->getChild("max-hp")->hasAttribute("start-value") && parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) { throw megaglest_runtime_error("Unit " + name + " has both start-value and start-percentage for HP", true); } //startHpValue -- the *absolute* value to use for starting HP if(parametersNode->getChild("max-hp")->hasAttribute("start-value")) { //checkItemInVault(&(this->startEp),this->startEp); startHpValue= parametersNode->getChild("max-hp")->getAttribute("start-value")->getIntValue(); startHpType= stValue; } addItemToVault(&(this->startHpValue),this->startHpValue); //startHpPercentage -- the *relative* value to use for starting HP if(parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) { startHpPercentage= parametersNode->getChild("max-hp")->getAttribute("start-percentage")->getIntValue(); startHpType= stPercentage; } // No start value is set; use 100% of the current Max if(!parametersNode->getChild("max-hp")->hasAttribute("start-value") && !parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) { startHpPercentage=100; startHpType= stPercentage; } addItemToVault(&(this->startHpPercentage),this->startHpPercentage); // Check that we don't use both start-value and start-percentage, as they are mutually // exclusive if(parametersNode->getChild("max-ep")->hasAttribute("start-value") && parametersNode->getChild("max-ep")->hasAttribute("start-percentage")) { throw megaglest_runtime_error("Unit " + name + " has both start-value and start-percentage for EP", true); } //startEpValue -- the *absolute* value to use for starting EP if(parametersNode->getChild("max-ep")->hasAttribute("start-value")) { //checkItemInVault(&(this->startEp),this->startEp); startEpValue= parametersNode->getChild("max-ep")->getAttribute("start-value")->getIntValue(); startEpType= stValue; } addItemToVault(&(this->startEpValue),this->startEpValue); //startEpPercentage -- the *relative* value to use for starting EP if(parametersNode->getChild("max-ep")->hasAttribute("start-percentage")) { startEpPercentage= parametersNode->getChild("max-ep")->getAttribute("start-percentage")->getIntValue(); startEpType= stPercentage; } addItemToVault(&(this->startEpPercentage),this->startEpPercentage); //maxUnitCount if(parametersNode->hasChild("max-unit-count")) { //checkItemInVault(&(this->maxUnitCount),this->maxUnitCount); maxUnitCount= parametersNode->getChild("max-unit-count")->getAttribute("value")->getIntValue(); } addItemToVault(&(this->maxUnitCount),this->maxUnitCount); //armor //checkItemInVault(&(this->armor),this->armor); armor= parametersNode->getChild("armor")->getAttribute("value")->getIntValue(); addItemToVault(&(this->armor),this->armor); //armor type string string armorTypeName= parametersNode->getChild("armor-type")->getAttribute("value")->getRestrictedValue(); armorType= techTree->getArmorType(armorTypeName); //sight //checkItemInVault(&(this->sight),this->sight); sight= parametersNode->getChild("sight")->getAttribute("value")->getIntValue(); addItemToVault(&(this->sight),this->sight); //prod time productionTime= parametersNode->getChild("time")->getAttribute("value")->getIntValue(); //multi selection multiSelect= parametersNode->getChild("multi-selection")->getAttribute("value")->getBoolValue(); //uniform selection if(parametersNode->hasChild("uniform-selection")) { uniformSelect= parametersNode->getChild("uniform-selection")->getAttribute("value")->getBoolValue(); } //commandable if(parametersNode->hasChild("commandable")){ commandable= parametersNode->getChild("commandable")->getAttribute("value")->getBoolValue(); } //cellmap allowEmptyCellMap = false; const XmlNode *cellMapNode= parametersNode->getChild("cellmap"); bool hasCellMap= cellMapNode->getAttribute("value")->getBoolValue(); if(hasCellMap == true) { if(cellMapNode->getAttribute("allowEmpty",false) != NULL) { allowEmptyCellMap = cellMapNode->getAttribute("allowEmpty")->getBoolValue(); } cellMap= new bool[size*size]; for(int i=0; igetChild("row", i); string row= rowNode->getAttribute("value")->getRestrictedValue(); if((int)row.size() != size){ throw megaglest_runtime_error("Cellmap row has not the same length as unit size",true); } for(int j=0; j < (int)row.size(); ++j){ cellMap[i*size+j]= row[j]=='0'? false: true; } } } //levels const XmlNode *levelsNode= parametersNode->getChild("levels"); levels.resize(levelsNode->getChildCount()); for(int i=0; i < (int)levels.size(); ++i){ const XmlNode *levelNode= levelsNode->getChild("level", i); levels[i].init( levelNode->getAttribute("name")->getRestrictedValue(), levelNode->getAttribute("kills")->getIntValue()); } //fields const XmlNode *fieldsNode= parametersNode->getChild("fields"); for(int i=0; i < (int)fieldsNode->getChildCount(); ++i){ const XmlNode *fieldNode= fieldsNode->getChild("field", i); string fieldName= fieldNode->getAttribute("value")->getRestrictedValue(); if(fieldName=="land"){ fields[fLand]= true; } else if(fieldName=="air"){ fields[fAir]= true; } else{ throw megaglest_runtime_error("Not a valid field: "+fieldName+": "+ path, true); } } if (fields[fLand]) { field = fLand; } else if (fields[fAir]) { field = fAir; } else { throw megaglest_runtime_error("Unit has no field: " + path, true); } //properties const XmlNode *propertiesNode= parametersNode->getChild("properties"); for(int i = 0; i < (int)propertiesNode->getChildCount(); ++i) { const XmlNode *propertyNode= propertiesNode->getChild("property", i); string propertyName= propertyNode->getAttribute("value")->getRestrictedValue(); bool found= false; for(int i = 0; i < pCount; ++i) { if(propertyName == propertyNames[i]) { properties[i]= true; found= true; break; } } if(!found) { throw megaglest_runtime_error("Unknown property: " + propertyName, true); } } //damage-particles if(parametersNode->hasChild("damage-particles")) { const XmlNode *particleNode= parametersNode->getChild("damage-particles"); bool particleEnabled= particleNode->getAttribute("value")->getBoolValue(); if(particleEnabled) { for(int i = 0; i < (int)particleNode->getChildCount(); ++i) { const XmlNode *particleFileNode= particleNode->getChild("particle-file", i); string path= particleFileNode->getAttribute("path")->getRestrictedValue(); UnitParticleSystemType *unitParticleSystemType= new UnitParticleSystemType(); unitParticleSystemType->load(particleFileNode, dir, currentPath + path, &Renderer::getInstance(),loadedFileList, sourceXMLFile, techTree->getPath()); loadedFileList[currentPath + path].push_back(make_pair(sourceXMLFile,particleFileNode->getAttribute("path")->getRestrictedValue())); if(particleFileNode->getAttribute("minHp",false) != NULL && particleFileNode->getAttribute("maxHp",false) != NULL) { unitParticleSystemType->setMinmaxEnabled(true); unitParticleSystemType->setMinHp(particleFileNode->getAttribute("minHp")->getIntValue()); unitParticleSystemType->setMaxHp(particleFileNode->getAttribute("maxHp")->getIntValue()); if(particleFileNode->getAttribute("ispercentbased",false) != NULL) { unitParticleSystemType->setMinmaxIsPercent(particleFileNode->getAttribute("ispercentbased")->getBoolValue()); } } damageParticleSystemTypes.push_back(unitParticleSystemType); } } } //healthbar if(parametersNode->hasChild("healthbar")) { const XmlNode *healthbarNode= parametersNode->getChild("healthbar"); if(healthbarNode->hasChild("height")) { healthbarheight= healthbarNode->getChild("height")->getAttribute("value")->getFloatValue(); } if(healthbarNode->hasChild("thickness")) { healthbarthickness= healthbarNode->getChild("thickness")->getAttribute("value")->getFloatValue(0.f, 1.f); } if(healthbarNode->hasChild("visible")) { string healthbarVisibleString=healthbarNode->getChild("visible")->getAttribute("value")->getValue(); vector v=split(healthbarVisibleString,"|"); for (int i = 0; i < (int)v.size(); ++i) { string current=trim(v[i]); if(current=="always") { healthbarVisible=healthbarVisible|hbvAlways; } else if(current=="selected") { healthbarVisible=healthbarVisible|hbvSelected; } else if(current=="ifNeeded") { healthbarVisible=healthbarVisible|hbvIfNeeded; } else if(current=="off") { healthbarVisible=healthbarVisible|hbvOff; } else { throw megaglest_runtime_error("Unknown Healthbar Visible Option: " + current, true); } } } } //light const XmlNode *lightNode= parametersNode->getChild("light"); light= lightNode->getAttribute("enabled")->getBoolValue(); if(light){ lightColor.x= lightNode->getAttribute("red")->getFloatValue(0.f, 1.f); lightColor.y= lightNode->getAttribute("green")->getFloatValue(0.f, 1.f); lightColor.z= lightNode->getAttribute("blue")->getFloatValue(0.f, 1.f); } //rotationAllowed if(parametersNode->hasChild("rotationAllowed")) { const XmlNode *rotationAllowedNode= parametersNode->getChild("rotationAllowed"); rotationAllowed= rotationAllowedNode->getAttribute("value")->getBoolValue(); } else { rotationAllowed=true; } std::map sortedItems; //unit requirements bool hasDup = false; const XmlNode *unitRequirementsNode= parametersNode->getChild("unit-requirements"); for(int i=0; i < (int)unitRequirementsNode->getChildCount(); ++i){ const XmlNode *unitNode= unitRequirementsNode->getChild("unit", i); string name= unitNode->getAttribute("name")->getRestrictedValue(); if(sortedItems.find(name) != sortedItems.end()) { hasDup = true; } sortedItems[name] = 0; } if(hasDup) { printf("WARNING, unit type [%s] has one or more duplicate unit requirements\n",this->getName(false).c_str()); } for(std::map::iterator iterMap = sortedItems.begin(); iterMap != sortedItems.end(); ++iterMap) { unitReqs.push_back(factionType->getUnitType(iterMap->first)); } sortedItems.clear(); hasDup = false; //upgrade requirements const XmlNode *upgradeRequirementsNode= parametersNode->getChild("upgrade-requirements"); for(int i=0; i < (int)upgradeRequirementsNode->getChildCount(); ++i){ const XmlNode *upgradeReqNode= upgradeRequirementsNode->getChild("upgrade", i); string name= upgradeReqNode->getAttribute("name")->getRestrictedValue(); if(sortedItems.find(name) != sortedItems.end()) { hasDup = true; } sortedItems[name] = 0; } if(hasDup) { printf("WARNING, unit type [%s] has one or more duplicate upgrade requirements\n",this->getName(false).c_str()); } for(std::map::iterator iterMap = sortedItems.begin(); iterMap != sortedItems.end(); ++iterMap) { upgradeReqs.push_back(factionType->getUpgradeType(iterMap->first)); } sortedItems.clear(); hasDup = false; //resource requirements const XmlNode *resourceRequirementsNode= parametersNode->getChild("resource-requirements"); costs.resize(resourceRequirementsNode->getChildCount()); for(int i = 0; i < (int)costs.size(); ++i) { const XmlNode *resourceNode= resourceRequirementsNode->getChild("resource", i); string name= resourceNode->getAttribute("name")->getRestrictedValue(); int amount= resourceNode->getAttribute("amount")->getIntValue(); if(sortedItems.find(name) != sortedItems.end()) { hasDup = true; } sortedItems[name] = amount; } //if(hasDup || sortedItems.size() != costs.size()) printf("Found duplicate resource requirement, costs.size() = %d sortedItems.size() = %d\n",costs.size(),sortedItems.size()); if(hasDup) { printf("WARNING, unit type [%s] has one or more duplicate resource requirements\n",this->getName(false).c_str()); } if(sortedItems.size() < costs.size()) { costs.resize(sortedItems.size()); } int index = 0; for(std::map::iterator iterMap = sortedItems.begin(); iterMap != sortedItems.end(); ++iterMap) { try { costs[index].init(techTree->getResourceType(iterMap->first), iterMap->second); index++; } catch(megaglest_runtime_error& ex) { if(validationMode == false) { throw; } else { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s Cost: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),iterMap->second); } } } sortedItems.clear(); hasDup = false; //resources stored const XmlNode *resourcesStoredNode= parametersNode->getChild("resources-stored"); storedResources.resize(resourcesStoredNode->getChildCount()); for(int i=0; i < (int)storedResources.size(); ++i){ const XmlNode *resourceNode= resourcesStoredNode->getChild("resource", i); string name= resourceNode->getAttribute("name")->getRestrictedValue(); int amount= resourceNode->getAttribute("amount")->getIntValue(); if(sortedItems.find(name) != sortedItems.end()) { hasDup = true; } sortedItems[name] = amount; } if(hasDup) { printf("WARNING, unit type [%s] has one or more duplicate stored resources\n",this->getName(false).c_str()); } if(sortedItems.size() < storedResources.size()) { storedResources.resize(sortedItems.size()); } index = 0; for(std::map::iterator iterMap = sortedItems.begin(); iterMap != sortedItems.end(); ++iterMap) { try { storedResources[index].init(techTree->getResourceType(iterMap->first), iterMap->second); index++; } catch(megaglest_runtime_error& ex) { if(validationMode == false) { throw; } else { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s Store: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),iterMap->second); } } } sortedItems.clear(); //hasDup = false; // Lootable resources (resources given/lost on death) if(parametersNode->hasChild("resources-death")) { const XmlNode *deathResourcesNode= parametersNode->getChild("resources-death"); for(unsigned int i = 0; i < deathResourcesNode->getChildCount(); ++i){ const XmlNode *resourceNode= deathResourcesNode->getChild("resource", i); string name= resourceNode->getAttribute("name")->getRestrictedValue(); LootableResource resource; resource.setResourceType(techTree->getResourceType(name)); // All attributes are optional, although nothing happens if they aren't used. They can // be combined freely. Percentages will take affect before absolute values. if(resourceNode->hasAttribute("amount-value")) { resource.setAmountValue(resourceNode->getAttribute("amount-value")->getIntValue()); } else { resource.setAmountValue(0); } if(resourceNode->hasAttribute("amount-faction-percent")) { resource.setAmountFactionPercent(resourceNode->getAttribute("amount-faction-percent")->getIntValue()); } else { resource.setAmountFactionPercent(0); } if(resourceNode->hasAttribute("loss-value")) { resource.setLossValue(resourceNode->getAttribute("loss-value")->getIntValue()); } else { resource.setLossValue(0); } if(resourceNode->hasAttribute("loss-faction-percent")) { resource.setLossFactionPercent(resourceNode->getAttribute("loss-faction-percent")->getIntValue()); } else { resource.setLossFactionPercent(0); } if(resourceNode->hasAttribute("allow-negative")) { resource.setNegativeAllowed(resourceNode->getAttribute("allow-negative")->getBoolValue()); } else { resource.setNegativeAllowed(false); } // Figure out if there are duplicate resources. The value stored in the map is arbitrary, // and exists solely because if(std::find(lootableResources.begin(), lootableResources.end(), resource) != lootableResources.end()) { printf("WARNING, unit type [%s] has one or more duplicate lootable resources\n", this->getName(false).c_str()); } lootableResources.push_back(resource); } } // Tags if(parametersNode->hasChild("tags")) { const XmlNode *tagsNode= parametersNode->getChild("tags"); for(unsigned int i = 0; i < tagsNode->getChildCount(); ++i){ const XmlNode *resourceNode= tagsNode->getChild("tag", i); string tag= resourceNode->getAttribute("value")->getRestrictedValue(); tags.insert(tag); } } //image const XmlNode *imageNode= parametersNode->getChild("image"); image= Renderer::getInstance().newTexture2D(rsGame); if(image) { image->load(imageNode->getAttribute("path")->getRestrictedValue(currentPath)); } loadedFileList[imageNode->getAttribute("path")->getRestrictedValue(currentPath)].push_back(make_pair(sourceXMLFile,imageNode->getAttribute("path")->getRestrictedValue())); //image cancel const XmlNode *imageCancelNode= parametersNode->getChild("image-cancel"); cancelImage= Renderer::getInstance().newTexture2D(rsGame); if(cancelImage) { cancelImage->load(imageCancelNode->getAttribute("path")->getRestrictedValue(currentPath)); } loadedFileList[imageCancelNode->getAttribute("path")->getRestrictedValue(currentPath)].push_back(make_pair(sourceXMLFile,imageCancelNode->getAttribute("path")->getRestrictedValue())); //meeting point const XmlNode *meetingPointNode= parametersNode->getChild("meeting-point"); meetingPoint= meetingPointNode->getAttribute("value")->getBoolValue(); if(meetingPoint) { meetingPointImage= Renderer::getInstance().newTexture2D(rsGame); if(meetingPointImage) { meetingPointImage->load(meetingPointNode->getAttribute("image-path")->getRestrictedValue(currentPath)); } loadedFileList[meetingPointNode->getAttribute("image-path")->getRestrictedValue(currentPath)].push_back(make_pair(sourceXMLFile,meetingPointNode->getAttribute("image-path")->getRestrictedValue())); } //countUnitDeathInStats if(parametersNode->hasChild("count-unit-death-in-stats")){ const XmlNode *countUnitDeathInStatsNode= parametersNode->getChild("count-unit-death-in-stats"); countUnitDeathInStats= countUnitDeathInStatsNode->getAttribute("value")->getBoolValue(); } else { countUnitDeathInStats=true; } //countUnitProductionInStats if(parametersNode->hasChild("count-unit-production-in-stats")){ const XmlNode *countUnitProductionInStatsNode= parametersNode->getChild("count-unit-production-in-stats"); countUnitProductionInStats= countUnitProductionInStatsNode->getAttribute("value")->getBoolValue(); } else { countUnitProductionInStats=true; } //countUnitKillInStats if(parametersNode->hasChild("count-unit-kill-in-stats")){ const XmlNode *countUnitKillInStatsNode= parametersNode->getChild("count-unit-kill-in-stats"); countUnitKillInStats= countUnitKillInStatsNode->getAttribute("value")->getBoolValue(); } else { countUnitKillInStats=true; } //countKillForUnitUpgrade if(parametersNode->hasChild("count-kill-for-unit-upgrade")){ const XmlNode *countKillForUnitUpgradeNode= parametersNode->getChild("count-kill-for-unit-upgrade"); countKillForUnitUpgrade= countKillForUnitUpgradeNode->getAttribute("value")->getBoolValue(); } else { countKillForUnitUpgrade=true; } if(countKillForUnitUpgrade == false){ // it makes no sense if we count it in stats but not for upgrades countUnitKillInStats=false; } //selection sounds const XmlNode *selectionSoundNode= parametersNode->getChild("selection-sounds"); if(selectionSoundNode->getAttribute("enabled")->getBoolValue()){ selectionSounds.resize((int)selectionSoundNode->getChildCount()); for(int i = 0; i < (int)selectionSounds.getSounds().size(); ++i) { const XmlNode *soundNode= selectionSoundNode->getChild("sound", i); string path= soundNode->getAttribute("path")->getRestrictedValue(currentPath); StaticSound *sound= new StaticSound(); sound->load(path); loadedFileList[path].push_back(make_pair(sourceXMLFile,soundNode->getAttribute("path")->getRestrictedValue())); selectionSounds[i]= sound; } } //command sounds const XmlNode *commandSoundNode= parametersNode->getChild("command-sounds"); if(commandSoundNode->getAttribute("enabled")->getBoolValue()) { commandSounds.resize((int)commandSoundNode->getChildCount()); for(int i = 0; i < (int)commandSoundNode->getChildCount(); ++i) { const XmlNode *soundNode= commandSoundNode->getChild("sound", i); string path= soundNode->getAttribute("path")->getRestrictedValue(currentPath); StaticSound *sound= new StaticSound(); sound->load(path); loadedFileList[path].push_back(make_pair(sourceXMLFile,soundNode->getAttribute("path")->getRestrictedValue())); commandSounds[i]= sound; } } //skills const XmlNode *attackBoostsNode= NULL; if(unitNode->hasChild("attack-boosts") == true) { attackBoostsNode=unitNode->getChild("attack-boosts"); } const XmlNode *skillsNode= unitNode->getChild("skills"); skillTypes.resize(skillsNode->getChildCount()); snprintf(szBuf,8096,Lang::getInstance().getString("LogScreenGameLoadingUnitTypeSkills","",true).c_str(),formatString(this->getName(true)).c_str(),skillTypes.size()); Logger::getInstance().add(szBuf, true); for(int i = 0; i < (int)skillTypes.size(); ++i) { const XmlNode *sn= skillsNode->getChild("skill", i); const XmlNode *typeNode= sn->getChild("type"); string classId= typeNode->getAttribute("value")->getRestrictedValue(); SkillType *skillType= SkillTypeFactory::getInstance().newInstance(classId); skillTypes[i]=NULL; try { skillType->load(sn, attackBoostsNode, dir, techTree, factionType, loadedFileList,sourceXMLFile); skillTypes[i]= skillType; } catch(megaglest_runtime_error& ex) { if(validationMode == false) { throw; } else { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s SkillType: %s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),classId.c_str()); } } } //commands const XmlNode *commandsNode= unitNode->getChild("commands"); commandTypes.resize(commandsNode->getChildCount()); for(int i = 0; i < (int)commandTypes.size(); ++i) { const XmlNode *commandNode= commandsNode->getChild("command", i); const XmlNode *typeNode= commandNode->getChild("type"); string classId= typeNode->getAttribute("value")->getRestrictedValue(); CommandType *commandType= CommandTypeFactory::getInstance().newInstance(classId); commandTypes[i]=NULL; try { commandType->load(i, commandNode, dir, techTree, factionType, *this, loadedFileList,sourceXMLFile); commandTypes[i]= commandType; } catch(megaglest_runtime_error& ex) { if(validationMode == false) { throw; } else { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\nFor UnitType: %s CommandType:%s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what(),name.c_str(),classId.c_str()); } } } sortCommandTypes(commandTypes); computeFirstStOfClass(); computeFirstCtOfClass(); if(getFirstStOfClass(scStop)==NULL){ throw megaglest_runtime_error("Every unit must have at least one stop skill: "+ path,true); } if(getFirstStOfClass(scDie)==NULL){ throw megaglest_runtime_error("Every unit must have at least one die skill: "+ path,true); } } //Exception handling (conversions and so on); 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 UnitType: " + path + "\nMessage: " + ex.what(),!ex.wantStackTrace()); } 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 UnitType: " + path + "\nMessage: " + e.what()); } if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); } // ==================== get ==================== const Level *UnitType::getLevel(string name) const { const Level *result = NULL; for(unsigned int i = 0; i < levels.size(); ++i) { const Level &level = levels[i]; if(level.getName() == name) { result = &level; break; } } return result; } const CommandType *UnitType::getFirstCtOfClass(CommandClass commandClass) const{ if(firstCommandTypeOfClass[commandClass] == NULL) { return NULL; //if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] commandClass = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,commandClass); /* for(int j=0; jgetClass()== CommandClass(j)){ return commandTypes[i]; } } } */ //if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); } return firstCommandTypeOfClass[commandClass]; } const SkillType *UnitType::getFirstStOfClass(SkillClass skillClass) const{ if(firstSkillTypeOfClass[skillClass] == NULL) { /* for(int j= 0; jgetClass()== SkillClass(j)){ return skillTypes[i]; } } } */ //SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__); } return firstSkillTypeOfClass[skillClass]; } const HarvestCommandType *UnitType::getFirstHarvestCommand(const ResourceType *resourceType, const Faction *faction) const { for(int i = 0; i < (int)commandTypes.size(); ++i) { if(commandTypes[i]->getClass() == ccHarvest) { const HarvestCommandType *hct = static_cast(commandTypes[i]); if (faction->reqsOk(hct) == false) { continue; } if(hct->canHarvest(resourceType)) { return hct; } } } return NULL; } const HarvestEmergencyReturnCommandType *UnitType::getFirstHarvestEmergencyReturnCommand() const { const HarvestEmergencyReturnCommandType *result = dynamic_cast(ctHarvestEmergencyReturnCommandType.get()); return result; } const AttackCommandType *UnitType::getFirstAttackCommand(Field field) const{ //printf("$$$ Unit [%s] commandTypes.size() = %d\n",this->getName().c_str(),(int)commandTypes.size()); for(int i = 0; i < (int)commandTypes.size(); ++i){ if(commandTypes[i] == NULL) { throw megaglest_runtime_error("commandTypes[i] == NULL"); } //printf("$$$ Unit [%s] i = %d, commandTypes[i] [%s]\n",this->getName().c_str(),(int)i, commandTypes[i]->toString().c_str()); if(commandTypes[i]->getClass()== ccAttack){ const AttackCommandType *act= dynamic_cast(commandTypes[i]); if(act != NULL && act->getAttackSkillType()->getAttackField(field)) { //printf("## Unit [%s] i = %d, is found\n",this->getName().c_str(),(int)i); return act; } } } return NULL; } const AttackStoppedCommandType *UnitType::getFirstAttackStoppedCommand(Field field) const{ //printf("$$$ Unit [%s] commandTypes.size() = %d\n",this->getName().c_str(),(int)commandTypes.size()); for(int i = 0; i < (int)commandTypes.size(); ++i){ if(commandTypes[i] == NULL) { throw megaglest_runtime_error("commandTypes[i] == NULL"); } //printf("$$$ Unit [%s] i = %d, commandTypes[i] [%s]\n",this->getName().c_str(),(int)i, commandTypes[i]->toString().c_str()); if(commandTypes[i]->getClass()== ccAttackStopped){ const AttackStoppedCommandType *act= dynamic_cast(commandTypes[i]); if(act != NULL && act->getAttackSkillType()->getAttackField(field)) { //printf("## Unit [%s] i = %d, is found\n",this->getName().c_str(),(int)i); return act; } } } return NULL; } const RepairCommandType *UnitType::getFirstRepairCommand(const UnitType *repaired) const{ for(int i=0; i < (int)commandTypes.size(); ++i){ if(commandTypes[i]->getClass()== ccRepair){ const RepairCommandType *rct= static_cast(commandTypes[i]); if(rct->isRepairableUnitType(repaired)){ return rct; } } } return NULL; } bool UnitType::hasEmptyCellMap() const { //checkItemInVault(&(this->size),this->size); bool result = (size > 0); for(int i = 0; result == true && i < size; ++i) { for(int j = 0; j < size; ++j){ if(cellMap[i*size+j] == true) { result = false; break; } } } return result; } Vec2i UnitType::getFirstOccupiedCellInCellMap(Vec2i currentPos) const { Vec2i cell = currentPos; //printf("\n\n\n\n^^^^^^^^^^ currentPos [%s] size [%d]\n",currentPos.getString().c_str(),size); //checkItemInVault(&(this->size),this->size); if(hasCellMap() == true) { for(int i = 0; i < size; ++i) { for(int j = 0; j < size; ++j){ if(cellMap[i*size+j] == true) { cell.x += i; cell.y += j; //printf("\n^^^^^^^^^^ cell [%s] i [%d] j [%d]\n",cell.getString().c_str(),i,j); return cell; } } } } return cell; } bool UnitType::getCellMapCell(int x, int y, CardinalDir facing) const { assert(cellMap); if(cellMap == NULL) { throw megaglest_runtime_error("cellMap == NULL"); } //checkItemInVault(&(this->size),this->size); int tmp=0; switch (facing) { case CardinalDir::EAST: tmp = y; y = x; x = size - tmp - 1; break; case CardinalDir::SOUTH: x = size - x - 1; y = size - y - 1; break; case CardinalDir::WEST: tmp = x; x = y; y = size - tmp - 1; break; default: break; } return cellMap[y * size + x]; } int UnitType::getStore(const ResourceType *rt) const{ for(int i=0; i < (int)storedResources.size(); ++i){ if(storedResources[i].getType()==rt){ return storedResources[i].getAmount(); } } return 0; } const SkillType *UnitType::getSkillType(const string &skillName, SkillClass skillClass) const{ for(int i=0; i < (int)skillTypes.size(); ++i){ if(skillTypes[i]->getName()==skillName){ if(skillTypes[i]->getClass()==skillClass){ return skillTypes[i]; } else{ throw megaglest_runtime_error("Skill \""+skillName+"\" is not of class \""+SkillType::skillClassToStr(skillClass)); } } } throw megaglest_runtime_error("No skill named \""+skillName+"\""); } // ==================== totals ==================== int UnitType::getTotalMaxHp(const TotalUpgrade *totalUpgrade) const { checkItemInVault(&(this->maxHp),this->maxHp); int result = maxHp + totalUpgrade->getMaxHp(); result = max(0,result); return result; } int UnitType::getTotalMaxHpRegeneration(const TotalUpgrade *totalUpgrade) const { checkItemInVault(&(this->hpRegeneration),this->hpRegeneration); int result = hpRegeneration + totalUpgrade->getMaxHpRegeneration(); //result = max(0,result); return result; } int UnitType::getTotalMaxEp(const TotalUpgrade *totalUpgrade) const { checkItemInVault(&(this->maxEp),this->maxEp); int result = maxEp + totalUpgrade->getMaxEp(); result = max(0,result); return result; } int UnitType::getTotalMaxEpRegeneration(const TotalUpgrade *totalUpgrade) const { checkItemInVault(&(this->epRegeneration),this->epRegeneration); int result = epRegeneration + totalUpgrade->getMaxEpRegeneration(); //result = max(0,result); return result; } int UnitType::getTotalArmor(const TotalUpgrade *totalUpgrade) const { checkItemInVault(&(this->armor),this->armor); int result = armor + totalUpgrade->getArmor(); result = max(0,result); return result; } int UnitType::getTotalSight(const TotalUpgrade *totalUpgrade) const { checkItemInVault(&(this->sight),this->sight); int result = sight + totalUpgrade->getSight(); result = max(0,result); return result; } // ==================== has ==================== bool UnitType::hasSkillClass(SkillClass skillClass) const { return firstSkillTypeOfClass[skillClass]!=NULL; } bool UnitType::hasCommandType(const CommandType *commandType) const { assert(commandType!=NULL); const HarvestEmergencyReturnCommandType *result = dynamic_cast(ctHarvestEmergencyReturnCommandType.get()); if(commandType == result) { return true; } for(int i=0; i < (int)commandTypes.size(); ++i) { if(commandTypes[i]==commandType) { return true; } } return false; } //bool UnitType::hasSkillType(const SkillType *skillType) const { // assert(skillType!=NULL); // for(int i=0; i < (int)skillTypes.size(); ++i) { // if(skillTypes[i]==skillType) { // return true; // } // } // return false; //} bool UnitType::isOfClass(UnitClass uc) const{ switch(uc){ case ucWarrior: return hasSkillClass(scAttack) && !hasSkillClass(scHarvest); case ucWorker: return hasSkillClass(scBuild) || hasSkillClass(scRepair)|| hasSkillClass(scHarvest); case ucBuilding: return hasSkillClass(scBeBuilt); default: assert(false); break; } return false; } // ==================== PRIVATE ==================== void UnitType::computeFirstStOfClass() { for(int j= 0; j < scCount; ++j) { firstSkillTypeOfClass[j]= NULL; for(int i= 0; i < (int)skillTypes.size(); ++i) { if(skillTypes[i] != NULL && skillTypes[i]->getClass()== SkillClass(j)) { firstSkillTypeOfClass[j]= skillTypes[i]; break; } } } } void UnitType::computeFirstCtOfClass() { for(int j = 0; j < ccCount; ++j) { firstCommandTypeOfClass[j]= NULL; for(int i = 0; i < (int)commandTypes.size(); ++i) { if(commandTypes[i] != NULL && commandTypes[i]->getClass() == CommandClass(j)) { firstCommandTypeOfClass[j] = commandTypes[i]; break; } } } } void UnitType::sortCommandTypes(CommandTypes cts){ try{ CommandTypes ctCores, ctBasics = {NULL,NULL,NULL,NULL}, ctMorphs, ctAttack; vector basicNulls = {0,1,2,3}; //Morphs for(int i = (int)cts.size(); i --> 0; ) { if(cts[i]->getClass() == ccMorph) { ctMorphs.insert(ctMorphs.begin(), cts[i]); cts.erase(cts.begin() + i); } } //Attacks CommandTypeFilter(cts, ctAttack, ccAttack); if(ctAttack.size() > 0) { ctBasics[CommandHelper::getBasicPos(ccAttack)] = ctAttack[0];// first attack to basics basicNulls.erase(basicNulls.begin()); ctAttack.erase(ctAttack.begin());// another to cores, see below } //Basics for(int i = (int)cts.size(); i --> 0; ) { for(auto &&cc : CommandHelper::getBasicsCC()){ if(cc == ccAttack) continue;// we catch all attacks above if(cts[i]->getClass() == cc) { auto ccPos = CommandHelper::getBasicPos(cc); ctBasics[ccPos] = cts[i]; cts.erase(cts.begin() + i); basicNulls.erase(std::remove(basicNulls.begin(), basicNulls.end(), ccPos), basicNulls.end()); } } } //Cores for(auto &&cc : CommandHelper::getCoresCC()){ if(cc == ccAttack) ctCores.insert(ctCores.end(), ctAttack.begin(), ctAttack.end()); else CommandTypeFilter(cts, ctCores, cc); } int nullCount = 4 - ctCores.size(); for(int i=0; i 0; ) {// we push it to basics ctBasics[basicNulls[i]] = ctToBasics[i]; basicNulls.erase(basicNulls.begin() + i); } } commandTypesSorted.insert(commandTypesSorted.end(), ctCores.begin(), ctCores.end()); commandTypesSorted.insert(commandTypesSorted.end(), ctBasics.begin(), ctBasics.end()); commandTypesSorted.insert(commandTypesSorted.end(), ctMorphs.begin(), ctMorphs.end()); } catch(exception &ex){ } } void UnitType::CommandTypeFilter(CommandTypes &input, CommandTypes &output, CommandClass cc){ std::copy_if(input.begin(), input.end(), std::back_inserter(output), [cc](CommandType* i) { if(i->getClass() == cc) return true; else return false; }); } const CommandType* UnitType::findCommandTypeById(int id) const{ const HarvestEmergencyReturnCommandType *result = dynamic_cast(ctHarvestEmergencyReturnCommandType.get()); if(result != NULL && id == result->getId()) { return result; } for(int i=0; i < getCommandTypeCount(); ++i) { const CommandType *commandType= getCommandType(i); if(commandType->getId() == id){ return commandType; } } return NULL; } const CommandType *UnitType::getCommandType(int i) const { if(i >= (int)commandTypes.size()) { char szBuf[8096]=""; snprintf(szBuf,8096,"In [%s::%s Line: %d] i >= commandTypes.size(), i = %d, commandTypes.size() = " MG_SIZE_T_SPECIFIER "",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,commandTypes.size()); throw megaglest_runtime_error(szBuf); } return commandTypes[i]; } const CommandType *UnitType::getCommandTypeSorted(int i) const { if(i >= (int)commandTypesSorted.size()) { char szBuf[8096]=""; snprintf(szBuf,8096,"In [%s::%s Line: %d] i >= commandTypesSorted.size(), i = %d, commandTypesSorted.size() = " MG_SIZE_T_SPECIFIER "",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,commandTypesSorted.size()); throw megaglest_runtime_error(szBuf); } return commandTypesSorted[i]; } string UnitType::getCommandTypeListDesc() const { string desc = "Commands: "; for(int i=0; igetId()); + " toString: " + commandType->toString(false); } return desc; } string UnitType::getReqDesc(bool translatedValue) const{ Lang &lang= Lang::getInstance(); //string desc = "Limits: "; string resultTxt=""; checkItemInVault(&(this->maxUnitCount),this->maxUnitCount); if(getMaxUnitCount() > 0) { resultTxt += "\n" + lang.getString("MaxUnitCount") + " " + intToStr(getMaxUnitCount()); } if(resultTxt == "") return ProducibleType::getReqDesc(translatedValue); else return ProducibleType::getReqDesc(translatedValue)+"\n" + lang.getString("Limits") + " " + resultTxt; } string UnitType::getName(bool translatedValue) const { if(translatedValue == false) return name; Lang &lang = Lang::getInstance(); return lang.getTechTreeString("UnitTypeName_" + name,name.c_str()); } std::string UnitType::toString() const { std::string result = "Unit Name: [" + name + "] id = " + intToStr(id); result += " maxHp = " + intToStr(maxHp); result += " hpRegeneration = " + intToStr(hpRegeneration); result += " maxEp = " + intToStr(maxEp); result += " startEpValue = " + intToStr(startEpValue); result += " startEpPercentage = " + intToStr(startEpPercentage); result += " epRegeneration = " + intToStr(epRegeneration); result += " maxUnitCount = " + intToStr(getMaxUnitCount()); for(int i = 0; i < fieldCount; i++) { result += " fields index = " + intToStr(i) + " value = " + intToStr(fields[i]); } for(int i = 0; i < pCount; i++) { result += " properties index = " + intToStr(i) + " value = " + intToStr(properties[i]); } result += " armor = " + intToStr(armor); if(armorType != NULL) { result += " armorType Name: [" + armorType->getName(false) + " id = " + intToStr(armorType->getId()); } result += " light = " + intToStr(light); result += " lightColor = " + lightColor.getString(); result += " multiSelect = " + intToStr(multiSelect); result += " uniformSelect = " + intToStr(uniformSelect); result += " commandable = " + intToStr(commandable); result += " sight = " + intToStr(sight); result += " size = " + intToStr(size); result += " height = " + intToStr(height); result += " rotatedBuildPos = " + floatToStr(rotatedBuildPos,6); result += " rotationAllowed = " + intToStr(rotationAllowed); if(cellMap != NULL) { result += " cellMap: [" + intToStr(size) + "]"; for(int i = 0; i < size; ++i) { for(int j = 0; j < size; ++j){ result += " i = " + intToStr(i) + " j = " + intToStr(j) + " value = " + intToStr(cellMap[i*size+j]); } } } result += " skillTypes: [" + intToStr(skillTypes.size()) + "]"; for(int i = 0; i < (int)skillTypes.size(); ++i) { result += " i = " + intToStr(i) + " " + skillTypes[i]->toString(false); } result += " commandTypes: [" + intToStr(commandTypes.size()) + "]"; for(int i = 0; i < (int)commandTypes.size(); ++i) { result += " i = " + intToStr(i) + " " + commandTypes[i]->toString(false); } result += " storedResources: [" + intToStr(storedResources.size()) + "]"; for(int i = 0; i < (int)storedResources.size(); ++i) { result += " i = " + intToStr(i) + " " + storedResources[i].getDescription(false); } result += " levels: [" + intToStr(levels.size()) + "]"; for(int i = 0; i < (int)levels.size(); ++i) { result += " i = " + intToStr(i) + " " + levels[i].getName(); } result += " meetingPoint = " + intToStr(meetingPoint); result += " countInVictoryConditions = " + intToStr(countInVictoryConditions); return result; } }}//end namespace