New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: tags #25
Feature: tags #25
Changes from all commits
b7e171e
15f251d
1c17d66
f37f17c
b80ef94
7336a20
c7bd35f
d1b4791
a19832d
bb69389
4a796d1
ab76a77
4c24161
7189584
4484344
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| @@ -61,34 +61,16 @@ bool AttackBoost::isAffected(const Unit *source, const Unit *dest) const { | ||
| else { | ||
| // All units are affected (including enemies) | ||
| if(targetType == abtAll) { | ||
| destUnitMightApply = (boostUnitList.empty() == true); | ||
|
|
||
| // Specify which units are affected | ||
| for(unsigned int i = 0; i < boostUnitList.size(); ++i) { | ||
| const UnitType *ut = boostUnitList[i]; | ||
| if(dest->getType()->getId() == ut->getId()) { | ||
| destUnitMightApply = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| destUnitMightApply = (boostUnitList.empty() && tags.empty()); | ||
| destUnitMightApply = isInUnitListOrTags(dest->getType()); | ||
| } | ||
| // Only same faction units are affected | ||
| else if(targetType == abtFaction) { | ||
| //if(boostUnitList.empty() == true) { | ||
| if(source->getFactionIndex() == dest->getFactionIndex()) { | ||
| //destUnitMightApply = true; | ||
| destUnitMightApply = (boostUnitList.empty() == true); | ||
|
|
||
| // Specify which units are affected | ||
| for(unsigned int i = 0; i < boostUnitList.size(); ++i) { | ||
| const UnitType *ut = boostUnitList[i]; | ||
| if(dest->getType()->getId() == ut->getId()) { | ||
| destUnitMightApply = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| destUnitMightApply = (boostUnitList.empty() && tags.empty()); | ||
| destUnitMightApply = isInUnitListOrTags(dest->getType()); | ||
| } | ||
| //} | ||
| } | ||
| @@ -97,16 +79,8 @@ bool AttackBoost::isAffected(const Unit *source, const Unit *dest) const { | ||
| //if(boostUnitList.empty() == true) { | ||
| if(source->isAlly(dest) == true) { | ||
| //destUnitMightApply = true; | ||
| destUnitMightApply = (boostUnitList.empty() == true); | ||
|
|
||
| // Specify which units are affected | ||
| for(unsigned int i = 0; i < boostUnitList.size(); ++i) { | ||
| const UnitType *ut = boostUnitList[i]; | ||
| if(dest->getType()->getId() == ut->getId()) { | ||
| destUnitMightApply = true; | ||
| break; | ||
| } | ||
| } | ||
| destUnitMightApply = (boostUnitList.empty() && tags.empty()); | ||
| destUnitMightApply = isInUnitListOrTags(dest->getType()); | ||
| } | ||
| //} | ||
| } | ||
| @@ -115,28 +89,13 @@ bool AttackBoost::isAffected(const Unit *source, const Unit *dest) const { | ||
| //if(boostUnitList.empty() == true) { | ||
| if(source->isAlly(dest) == false) { | ||
| //destUnitMightApply = true; | ||
| destUnitMightApply = (boostUnitList.empty() == true); | ||
|
|
||
| // Specify which units are affected | ||
| for(unsigned int i = 0; i < boostUnitList.size(); ++i) { | ||
| const UnitType *ut = boostUnitList[i]; | ||
| if(dest->getType()->getId() == ut->getId()) { | ||
| destUnitMightApply = true; | ||
| break; | ||
| } | ||
| } | ||
| destUnitMightApply = (boostUnitList.empty() && tags.empty()); | ||
| destUnitMightApply = isInUnitListOrTags(dest->getType()); | ||
| } | ||
| //} | ||
| } | ||
| else if(targetType == abtUnitTypes) { | ||
| // Specify which units are affected | ||
| for(unsigned int i = 0; i < boostUnitList.size(); ++i) { | ||
| const UnitType *ut = boostUnitList[i]; | ||
| if(dest->getType()->getId() == ut->getId()) { | ||
| destUnitMightApply = true; | ||
| break; | ||
| } | ||
| } | ||
| destUnitMightApply = isInUnitListOrTags(dest->getType()); | ||
| } | ||
| } | ||
|
|
||
| @@ -151,6 +110,32 @@ bool AttackBoost::isAffected(const Unit *source, const Unit *dest) const { | ||
| return result; | ||
| } | ||
|
|
||
| bool AttackBoost::isInUnitListOrTags(const UnitType *unitType) const { | ||
| // Specify which units are affected | ||
| std::set<const UnitType*>::iterator it; | ||
| for (it = boostUnitList.begin(); it != boostUnitList.end(); ++it) { | ||
| const UnitType *boostUnit = *it; | ||
| if(unitType->getId() == boostUnit->getId()) { | ||
| return true; | ||
| } | ||
| } | ||
| set<string> unitTags = unitType->getTags(); | ||
| set<string> intersect; | ||
| set_intersection(tags.begin(),tags.end(),unitTags.begin(),unitTags.end(), | ||
| std::inserter(intersect,intersect.begin())); | ||
| if(!intersect.empty()) return true; | ||
|
|
||
| // Otherwise no match | ||
| return false; | ||
| } | ||
|
|
||
| string AttackBoost::getTagName(string tag, bool translatedValue) const { | ||
| if(translatedValue == false) return tag; | ||
|
|
||
| Lang &lang = Lang::getInstance(); | ||
| return lang.getTechTreeString("TagName_" + tag, tag.c_str()); | ||
| } | ||
|
|
||
| string AttackBoost::getDesc(bool translatedValue) const{ | ||
| Lang &lang= Lang::getInstance(); | ||
| string str= ""; | ||
| @@ -190,14 +175,31 @@ string AttackBoost::getDesc(bool translatedValue) const{ | ||
| str+= lang.getString("AffectedUnitsFromAll") +":\n"; | ||
| } | ||
|
|
||
| if(boostUnitList.empty() == false) { | ||
| for(int i=0; i < (int)boostUnitList.size(); ++i){ | ||
| str+= " "+boostUnitList[i]->getName(translatedValue)+"\n"; | ||
| } | ||
| if(boostUnitList.empty() && tags.empty()) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that the logic of this condition has been switched around. Now the case where all units are affected comes first, since it's simpler. |
||
| str+= lang.getString("All")+"\n"; | ||
| } | ||
| else | ||
| { | ||
| str+= lang.getString("All")+"\n"; | ||
| // We want the output to be sorted, so convert the set to a vector and sort that | ||
| std::vector<const UnitType*> outputUnits(boostUnitList.begin(), boostUnitList.end()); | ||
| std::sort(outputUnits.begin(), outputUnits.end(), UnitTypeSorter()); | ||
|
|
||
| vector<const UnitType*>::iterator unitIter; | ||
| for (unitIter = outputUnits.begin(); unitIter != outputUnits.end(); ++unitIter) { | ||
| const UnitType *unit = *unitIter; | ||
| str+= indent+unit->getName(translatedValue)+"\n"; | ||
| } | ||
|
|
||
| // Do the same for tags | ||
| std::vector<string> outputTags(tags.begin(), tags.end()); | ||
| std::sort(outputTags.begin(), outputTags.end()); | ||
|
|
||
| vector<string>::iterator tagIter; | ||
| for (tagIter = outputTags.begin(); tagIter != outputTags.end(); ++tagIter) { | ||
| string tag = *tagIter; | ||
| str+= indent + lang.getString("TagDesc", (translatedValue == true ? "" : "english")) + | ||
| " " + getTagName(tag,translatedValue) + "\n"; | ||
| } | ||
| } | ||
|
|
||
| return str; | ||
| @@ -222,10 +224,18 @@ void AttackBoost::loadGame(const XmlNode *rootNode, Faction *faction, const Skil | ||
| string unitTypeName = node->getAttribute("name")->getValue(); | ||
| const UnitType *unitType = faction->getType()->getUnitType(unitTypeName); | ||
| if(unitType != NULL) { | ||
| boostUnitList.push_back(unitType); | ||
| boostUnitList.insert(unitType); | ||
| } | ||
| } | ||
| } | ||
| if(attackBoostNode->hasChild("tag")) { | ||
| vector<XmlNode *> tagNodeList = attackBoostNode->getChildList("tag"); | ||
| for(unsigned int i = 0; i < tagNodeList.size(); ++i) { | ||
| XmlNode *node = tagNodeList[i]; | ||
| string tagName = node->getAttribute("name")->getValue(); | ||
| tags.insert(tagName); | ||
| } | ||
| } | ||
| //boostUpgrade.loadGame(attackBoostNode,faction); | ||
| boostUpgrade = skillType->getAttackBoost()->boostUpgrade; | ||
|
|
||
| @@ -252,10 +262,17 @@ void AttackBoost::saveGame(XmlNode *rootNode) const { | ||
| // AttackBoostTargetType targetType; | ||
| attackBoostNode->addAttribute("targetType",intToStr(targetType), mapTagReplacements); | ||
| // vector<const UnitType *> boostUnitList; | ||
| for(unsigned int i = 0; i < boostUnitList.size(); ++i) { | ||
| const UnitType *ut = boostUnitList[i]; | ||
| std::set<const UnitType*>::iterator unitIter; | ||
| for (unitIter = boostUnitList.begin(); unitIter != boostUnitList.end(); ++unitIter) { | ||
| const UnitType *unit = *unitIter; | ||
| XmlNode *unitTypeNode = attackBoostNode->addChild("UnitType"); | ||
| unitTypeNode->addAttribute("name",ut->getName(false), mapTagReplacements); | ||
| unitTypeNode->addAttribute("name",unit->getName(false), mapTagReplacements); | ||
| } | ||
| std::set<string>::iterator tagIter; | ||
| for (tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { | ||
| string tag = *tagIter; | ||
| XmlNode *unitTypeNode = attackBoostNode->addChild("tag"); | ||
| unitTypeNode->addAttribute("name", tag, mapTagReplacements); | ||
| } | ||
| // UpgradeTypeBase boostUpgrade; | ||
| boostUpgrade.saveGame(attackBoostNode); | ||
| @@ -352,45 +369,40 @@ void SkillType::loadAttackBoost(const XmlNode *attackBoostsNode, const XmlNode * | ||
|
|
||
| if(targetType == "ally") { | ||
| attackBoost.targetType = abtAlly; | ||
| for(int i = 0;i < (int)attackBoostNode->getChild("target")->getChildCount();++i) { | ||
| const XmlNode *boostUnitNode = attackBoostNode->getChild("target")->getChild("unit-type", i); | ||
| attackBoost.boostUnitList.push_back(ft->getUnitType(boostUnitNode->getAttribute("name")->getRestrictedValue())); | ||
| } | ||
| } | ||
| else if(targetType == "foe") { | ||
| attackBoost.targetType = abtFoe; | ||
| for(int i = 0;i < (int)attackBoostNode->getChild("target")->getChildCount();++i) { | ||
| const XmlNode *boostUnitNode = attackBoostNode->getChild("target")->getChild("unit-type", i); | ||
| attackBoost.boostUnitList.push_back(ft->getUnitType(boostUnitNode->getAttribute("name")->getRestrictedValue())); | ||
| } | ||
| } | ||
| else if(targetType == "faction") { | ||
| attackBoost.targetType = abtFaction; | ||
| for(int i = 0;i < (int)attackBoostNode->getChild("target")->getChildCount();++i) { | ||
| const XmlNode *boostUnitNode = attackBoostNode->getChild("target")->getChild("unit-type", i); | ||
| attackBoost.boostUnitList.push_back(ft->getUnitType(boostUnitNode->getAttribute("name")->getRestrictedValue())); | ||
| } | ||
| } | ||
| else if(targetType == "unit-types") { | ||
| attackBoost.targetType = abtUnitTypes; | ||
| for(int i = 0;i < (int)attackBoostNode->getChild("target")->getChildCount();++i) { | ||
| const XmlNode *boostUnitNode = attackBoostNode->getChild("target")->getChild("unit-type", i); | ||
| attackBoost.boostUnitList.push_back(ft->getUnitType(boostUnitNode->getAttribute("name")->getRestrictedValue())); | ||
| } | ||
| } | ||
| else if(targetType == "all") { | ||
| attackBoost.targetType = abtAll; | ||
| for(int i = 0;i < (int)attackBoostNode->getChild("target")->getChildCount();++i) { | ||
| const XmlNode *boostUnitNode = attackBoostNode->getChild("target")->getChild("unit-type", i); | ||
| attackBoost.boostUnitList.push_back(ft->getUnitType(boostUnitNode->getAttribute("name")->getRestrictedValue())); | ||
| } | ||
| } | ||
| else { | ||
| char szBuf[8096] = ""; | ||
| snprintf(szBuf, 8096,"Unsupported target [%s] specified for attack boost for skill [%s] in [%s]", targetType.c_str(), name.c_str(), parentLoader.c_str()); | ||
| throw megaglest_runtime_error(szBuf); | ||
| } | ||
|
|
||
| // Load the regular targets | ||
| const XmlNode *targetNode = attackBoostNode->getChild("target"); | ||
| vector<XmlNode*> targetNodes = targetNode->getChildList("unit-type"); | ||
| for(size_t i = 0;i < targetNodes.size(); ++i) { | ||
| string unitName = targetNodes.at(i)->getAttribute("name")->getRestrictedValue(); | ||
| attackBoost.boostUnitList.insert(ft->getUnitType(unitName)); | ||
| } | ||
|
|
||
| // Load tags | ||
| vector<XmlNode*> tagNodes = targetNode->getChildList("tag"); | ||
| for(size_t i = 0;i < tagNodes.size(); ++i) { | ||
| string unitName = tagNodes.at(i)->getAttribute("name")->getRestrictedValue(); | ||
| attackBoost.tags.insert(unitName); | ||
| } | ||
|
|
||
| attackBoost.boostUpgrade.load(attackBoostNode,attackBoost.name); | ||
| if(attackBoostNode->hasChild("particles") == true) { | ||
| const XmlNode *particleNode = attackBoostNode->getChild("particles"); | ||
| @@ -30,6 +30,7 @@ | ||
| #include "projectile_type.h" | ||
| #include "upgrade_type.h" | ||
| #include "leak_dumper.h" | ||
| #include <set> | ||
|
|
||
| using Shared::Sound::StaticSound; | ||
| using Shared::Xml::XmlNode; | ||
| @@ -98,7 +99,8 @@ class AttackBoost { | ||
| bool allowMultipleBoosts; | ||
| int radius; | ||
| AttackBoostTargetType targetType; | ||
| vector<const UnitType *> boostUnitList; | ||
| std::set<const UnitType *> boostUnitList; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sets are used to simplify handling duplicates. Sets will ignore duplicates. This was a bigger deal back when the implementation was to simply dump all units with a matching tag into the boostUnitList. Although now it's useful to find the intercept of the set of upgrade/attack-boost tags and unit tags, which makes checking if a unit is affected much simpler (if slightly less efficient). |
||
| std::set<string> tags; | ||
| UpgradeTypeBase boostUpgrade; | ||
|
|
||
| UnitParticleSystemType *unitParticleSystemTypeForSourceUnit; | ||
| @@ -109,9 +111,20 @@ class AttackBoost { | ||
|
|
||
| bool isAffected(const Unit *source, const Unit *dest) const; | ||
| virtual string getDesc(bool translatedValue) const; | ||
| string getTagName(string tag, bool translatedValue=false) const; | ||
|
|
||
| virtual void saveGame(XmlNode *rootNode) const; | ||
| virtual void loadGame(const XmlNode *rootNode, Faction *faction, const SkillType *skillType); | ||
|
|
||
| private: | ||
| /** | ||
| * Checks if a unit is affected by the attack boost by checking if either the UnitType is in | ||
| * the #boostUnitList or shares a tag with #tags. | ||
| * @param unitType The unit type to check. | ||
| * @return True if the unit *might* be affected by the attack boost (still have to check if it's | ||
| * in range), false otherwise. | ||
| */ | ||
| bool isInUnitListOrTags(const UnitType *unitType) const; | ||
| }; | ||
|
|
||
| class AnimationAttributes { | ||
| @@ -192,6 +192,7 @@ class UnitType: public ProducibleType, public ValueCheckerVault { | ||
| StoredResources storedResources; | ||
| Levels levels; | ||
| LootableResources lootableResources; | ||
| std::set<string> tags; | ||
|
|
||
| //meeting point | ||
| bool meetingPoint; | ||
| @@ -263,6 +264,7 @@ class UnitType: public ProducibleType, public ValueCheckerVault { | ||
| inline const Resource *getStoredResource(int i) const {return &storedResources[i];} | ||
| int getLootableResourceCount() const {return lootableResources.size();} | ||
| inline const LootableResource getLootableResource(int i) const {return lootableResources.at(i);} | ||
| const set<string> &getTags() const {return tags;} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This gave me much grief. This used to return a value, which causes issues with using iterators. The issue is when you use an iterator like so: for(auto it = unitType->getTags().begin(); it != unitType->getTags().end(); ++it)The issue is that Changing |
||
| bool getCellMapCell(int x, int y, CardinalDir facing) const; | ||
| inline bool getMeetingPoint() const {return meetingPoint;} | ||
| inline bool getCountUnitDeathInStats() const {return countUnitDeathInStats;} | ||
| @@ -327,6 +329,17 @@ class UnitType: public ProducibleType, public ValueCheckerVault { | ||
| void computeFirstCtOfClass(); | ||
| }; | ||
|
|
||
| /** | ||
| * Used to sort UnitType. Sorts by *translated* unit name. Sorting is case sensitive and done in | ||
| * lexical order. | ||
| */ | ||
| struct UnitTypeSorter | ||
| { | ||
| bool operator()( const UnitType *left, const UnitType *right ) const { | ||
| return left->getName(true) < right->getName(true); | ||
| } | ||
| }; | ||
|
|
||
| }}//end namespace | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bodies of these if statements have a number of code clones, which have been extracted into
isInUnitListOrTags(const UnitType*).