diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 4ed544c20..006d67bfa 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -337,12 +337,40 @@ void CCreature::addBonus(int val, BonusType type, BonusSubtypeID subtype) } } -bool CCreature::isMyUpgrade(const CCreature *anotherCre) const +bool CCreature::isMyDirectUpgrade(const CCreature *anotherCre) const { - //TODO upgrade of upgrade? return vstd::contains(upgrades, anotherCre->getId()); } +bool CCreature::isMyDirectOrIndirectUpgrade(const CCreature *anotherCre) const +{ + std::set foundUpgrades; + std::vector upgradesToTest; + + upgradesToTest.push_back(getId()); + + while (!upgradesToTest.empty()) + { + CreatureID testedID = upgradesToTest.back(); + const CCreature * testedPtr = testedID.toCreature(); + + upgradesToTest.pop_back(); + + for (const auto & upgrade : testedPtr->upgrades) + { + if (upgrade == anotherCre->getId()) + return true; + + if (foundUpgrades.count(upgrade)) + continue; + + upgradesToTest.push_back(upgrade); + foundUpgrades.insert(upgrade); + } + } + return false; +} + std::string CCreature::nodeName() const { return "\"" + getNamePluralTextID() + "\""; diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index ca95b2689..e1d812fa8 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -163,7 +163,13 @@ public: static CCreature::CreatureQuantityId getQuantityID(const int & quantity); static std::string getQuantityRangeStringForId(const CCreature::CreatureQuantityId & quantityId); static int estimateCreatureCount(ui32 countID); //reverse version of above function, returns middle of range - bool isMyUpgrade(const CCreature *anotherCre) const; + + /// Returns true if this creature can be directly upgraded to target + bool isMyDirectUpgrade(const CCreature * target) const; + + /// Returns true if this creature can be upgraded to target + /// Performs full search through potential upgrades of upgrades + bool isMyDirectOrIndirectUpgrade(const CCreature *target) const; void addBonus(int val, BonusType type); void addBonus(int val, BonusType type, BonusSubtypeID subtype); diff --git a/lib/bonuses/Limiters.cpp b/lib/bonuses/Limiters.cpp index 984eb068c..01fc45627 100644 --- a/lib/bonuses/Limiters.cpp +++ b/lib/bonuses/Limiters.cpp @@ -105,7 +105,7 @@ ILimiter::EDecision CCreatureTypeLimiter::limit(const BonusLimitationContext &co if(!c) return ILimiter::EDecision::NOT_APPLICABLE; - auto accept = c->getId() == creatureID || (includeUpgrades && creatureID.toCreature()->isMyUpgrade(c)); + auto accept = c->getId() == creatureID || (includeUpgrades && creatureID.toCreature()->isMyDirectOrIndirectUpgrade(c)); return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD; //drop bonus if it's not our creature and (we don`t check upgrades or its not our upgrade) } diff --git a/lib/entities/hero/CHeroHandler.cpp b/lib/entities/hero/CHeroHandler.cpp index 307017c43..c97725372 100644 --- a/lib/entities/hero/CHeroHandler.cpp +++ b/lib/entities/hero/CHeroHandler.cpp @@ -140,59 +140,39 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) const } /// creates standard H3 hero specialty for creatures -static std::vector> createCreatureSpecialty(CreatureID baseCreatureID) +static std::vector> createCreatureSpecialty(CreatureID cid) { std::vector> result; - std::set targets; - targets.insert(baseCreatureID); - // go through entire upgrade chain and collect all creatures to which baseCreatureID can be upgraded - for (;;) + const auto & specCreature = *cid.toCreature(); + int stepSize = specCreature.getLevel() ? specCreature.getLevel() : 5; + { - std::set oldTargets = targets; - - for(const auto & upgradeSourceID : oldTargets) - { - const CCreature * upgradeSource = upgradeSourceID.toCreature(); - targets.insert(upgradeSource->upgrades.begin(), upgradeSource->upgrades.end()); - } - - if (oldTargets.size() == targets.size()) - break; + auto bonus = std::make_shared(); + bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, true)); + bonus->type = BonusType::STACKS_SPEED; + bonus->val = 1; + result.push_back(bonus); } - for(CreatureID cid : targets) { - const auto & specCreature = *cid.toCreature(); - int stepSize = specCreature.getLevel() ? specCreature.getLevel() : 5; + auto bonus = std::make_shared(); + bonus->type = BonusType::PRIMARY_SKILL; + bonus->subtype = BonusSubtypeID(PrimarySkill::ATTACK); + bonus->val = 0; + bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, true)); + bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getAttack(false), stepSize)); + result.push_back(bonus); + } - { - auto bonus = std::make_shared(); - bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); - bonus->type = BonusType::STACKS_SPEED; - bonus->val = 1; - result.push_back(bonus); - } - - { - auto bonus = std::make_shared(); - bonus->type = BonusType::PRIMARY_SKILL; - bonus->subtype = BonusSubtypeID(PrimarySkill::ATTACK); - bonus->val = 0; - bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); - bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getAttack(false), stepSize)); - result.push_back(bonus); - } - - { - auto bonus = std::make_shared(); - bonus->type = BonusType::PRIMARY_SKILL; - bonus->subtype = BonusSubtypeID(PrimarySkill::DEFENSE); - bonus->val = 0; - bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); - bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize)); - result.push_back(bonus); - } + { + auto bonus = std::make_shared(); + bonus->type = BonusType::PRIMARY_SKILL; + bonus->subtype = BonusSubtypeID(PrimarySkill::DEFENSE); + bonus->val = 0; + bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, true)); + bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize)); + result.push_back(bonus); } return result; diff --git a/lib/mapObjects/CGCreature.cpp b/lib/mapObjects/CGCreature.cpp index 9fbdfe2c6..5717676b5 100644 --- a/lib/mapObjects/CGCreature.cpp +++ b/lib/mapObjects/CGCreature.cpp @@ -529,7 +529,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult & const CCreature * cre = getCreature(); for(i = stacks.begin(); i != stacks.end(); i++) { - if(cre->isMyUpgrade(i->second->getCreature())) + if(cre->isMyDirectUpgrade(i->second->getCreature())) { cb->changeStackType(StackLocation(id, i->first), cre); //un-upgrade creatures }