TargetCondition.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /*
  2. * TargetCondition.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "TargetCondition.h"
  12. #include "../GameConstants.h"
  13. #include "../CBonusTypeHandler.h"
  14. #include "../battle/CBattleInfoCallback.h"
  15. #include "../battle/Unit.h"
  16. #include "../bonuses/BonusParams.h"
  17. #include "../bonuses/BonusList.h"
  18. #include "../serializer/JsonSerializeFormat.h"
  19. #include "../VCMI_Lib.h"
  20. #include "../CModHandler.h"
  21. VCMI_LIB_NAMESPACE_BEGIN
  22. namespace spells
  23. {
  24. class TargetConditionItemBase : public TargetConditionItem
  25. {
  26. public:
  27. bool inverted = false;
  28. bool exclusive = false;
  29. void setInverted(bool value) override
  30. {
  31. inverted = value;
  32. }
  33. void setExclusive(bool value) override
  34. {
  35. exclusive = value;
  36. }
  37. bool isExclusive() const override
  38. {
  39. return exclusive;
  40. }
  41. bool isReceptive(const Mechanics * m, const battle::Unit * target) const override
  42. {
  43. bool result = check(m, target);
  44. return inverted != result;
  45. }
  46. protected:
  47. virtual bool check(const Mechanics * m, const battle::Unit * target) const = 0;
  48. };
  49. class SelectorCondition : public TargetConditionItemBase
  50. {
  51. public:
  52. SelectorCondition(const CSelector & csel):
  53. sel(csel)
  54. {
  55. }
  56. SelectorCondition(const CSelector & csel, si32 minVal, si32 maxVal):
  57. sel(csel),
  58. minVal(minVal),
  59. maxVal(maxVal)
  60. {
  61. }
  62. protected:
  63. bool check(const Mechanics * m, const battle::Unit * target) const override
  64. {
  65. if(target->hasBonus(sel)) {
  66. auto b = target->valOfBonuses(sel,"");
  67. return b >= minVal && b <= maxVal;
  68. }
  69. return false;
  70. }
  71. private:
  72. CSelector sel;
  73. si32 minVal = std::numeric_limits<si32>::min();
  74. si32 maxVal = std::numeric_limits<si32>::max();
  75. };
  76. class ResistanceCondition : public TargetConditionItemBase
  77. {
  78. protected:
  79. bool check(const Mechanics * m, const battle::Unit * target) const override
  80. {
  81. if(m->isPositiveSpell()) //Always pass on positive
  82. return true;
  83. return target->magicResistance() < 100;
  84. }
  85. };
  86. class CreatureCondition : public TargetConditionItemBase
  87. {
  88. public:
  89. CreatureCondition(const CreatureID & type_): type(type_) {}
  90. protected:
  91. bool check(const Mechanics * m, const battle::Unit * target) const override
  92. {
  93. return target->creatureId() == type;
  94. }
  95. private:
  96. CreatureID type;
  97. };
  98. class AbsoluteLevelCondition : public TargetConditionItemBase
  99. {
  100. public:
  101. AbsoluteLevelCondition()
  102. {
  103. inverted = false;
  104. exclusive = true;
  105. }
  106. protected:
  107. bool check(const Mechanics * m, const battle::Unit * target) const override
  108. {
  109. if(!m->isMagicalEffect()) //Always pass on non-magical
  110. return true;
  111. std::stringstream cachingStr;
  112. cachingStr << "type_" << vstd::to_underlying(BonusType::LEVEL_SPELL_IMMUNITY) << "addInfo_1";
  113. TConstBonusListPtr levelImmunities = target->getBonuses(Selector::type()(BonusType::LEVEL_SPELL_IMMUNITY).And(Selector::info()(1)), cachingStr.str());
  114. return (levelImmunities->size() == 0 || levelImmunities->totalValue() < m->getSpellLevel() || m->getSpellLevel() <= 0);
  115. }
  116. };
  117. class AbsoluteSpellCondition : public TargetConditionItemBase
  118. {
  119. public:
  120. AbsoluteSpellCondition()
  121. {
  122. inverted = false;
  123. exclusive = true;
  124. }
  125. protected:
  126. bool check(const Mechanics * m, const battle::Unit * target) const override
  127. {
  128. std::stringstream cachingStr;
  129. cachingStr << "type_" << vstd::to_underlying(BonusType::SPELL_IMMUNITY) << "subtype_" << m->getSpellIndex() << "addInfo_1";
  130. return !target->hasBonus(Selector::typeSubtypeInfo(BonusType::SPELL_IMMUNITY, m->getSpellIndex(), 1), cachingStr.str());
  131. }
  132. };
  133. class ElementalCondition : public TargetConditionItemBase
  134. {
  135. public:
  136. ElementalCondition()
  137. {
  138. inverted = true;
  139. exclusive = true;
  140. }
  141. protected:
  142. bool check(const Mechanics * m, const battle::Unit * target) const override
  143. {
  144. bool elementalImmune = false;
  145. auto filter = m->getElementalImmunity();
  146. for(auto element : filter)
  147. {
  148. if(target->hasBonusOfType(element, 0)) //always resist if immune to all spells altogether
  149. {
  150. elementalImmune = true;
  151. break;
  152. }
  153. else if(!m->isPositiveSpell()) //negative or indifferent
  154. {
  155. if(target->hasBonusOfType(element, 1))
  156. {
  157. elementalImmune = true;
  158. break;
  159. }
  160. }
  161. }
  162. return elementalImmune;
  163. }
  164. };
  165. class NormalLevelCondition : public TargetConditionItemBase
  166. {
  167. public:
  168. NormalLevelCondition()
  169. {
  170. inverted = false;
  171. exclusive = true;
  172. }
  173. protected:
  174. bool check(const Mechanics * m, const battle::Unit * target) const override
  175. {
  176. if(!m->isMagicalEffect()) //Always pass on non-magical
  177. return true;
  178. TConstBonusListPtr levelImmunities = target->getBonuses(Selector::type()(BonusType::LEVEL_SPELL_IMMUNITY));
  179. return levelImmunities->size() == 0 ||
  180. levelImmunities->totalValue() < m->getSpellLevel() ||
  181. m->getSpellLevel() <= 0;
  182. }
  183. };
  184. class NormalSpellCondition : public TargetConditionItemBase
  185. {
  186. public:
  187. NormalSpellCondition()
  188. {
  189. inverted = false;
  190. exclusive = true;
  191. }
  192. protected:
  193. bool check(const Mechanics * m, const battle::Unit * target) const override
  194. {
  195. return !target->hasBonusOfType(BonusType::SPELL_IMMUNITY, m->getSpellIndex());
  196. }
  197. };
  198. //for Hypnotize
  199. class HealthValueCondition : public TargetConditionItemBase
  200. {
  201. protected:
  202. bool check(const Mechanics * m, const battle::Unit * target) const override
  203. {
  204. //todo: maybe do not resist on passive cast
  205. //TODO: what with other creatures casting hypnotize, Faerie Dragons style?
  206. int64_t subjectHealth = target->getAvailableHealth();
  207. //apply 'damage' bonus for hypnotize, including hero specialty
  208. auto maxHealth = m->applySpellBonus(m->getEffectValue(), target);
  209. return subjectHealth <= maxHealth;
  210. }
  211. };
  212. class SpellEffectCondition : public TargetConditionItemBase
  213. {
  214. public:
  215. SpellEffectCondition(const SpellID & spellID_): spellID(spellID_)
  216. {
  217. std::stringstream builder;
  218. builder << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << spellID.num;
  219. cachingString = builder.str();
  220. selector = Selector::source(BonusSource::SPELL_EFFECT, spellID.num);
  221. }
  222. protected:
  223. bool check(const Mechanics * m, const battle::Unit * target) const override
  224. {
  225. return target->hasBonus(selector, cachingString);
  226. }
  227. private:
  228. CSelector selector;
  229. std::string cachingString;
  230. SpellID spellID;
  231. };
  232. class ReceptiveFeatureCondition : public TargetConditionItemBase
  233. {
  234. protected:
  235. bool check(const Mechanics * m, const battle::Unit * target) const override
  236. {
  237. return m->isPositiveSpell() && target->hasBonus(selector, cachingString);
  238. }
  239. private:
  240. CSelector selector = Selector::type()(BonusType::RECEPTIVE);
  241. std::string cachingString = "type_RECEPTIVE";
  242. };
  243. class ImmunityNegationCondition : public TargetConditionItemBase
  244. {
  245. protected:
  246. bool check(const Mechanics * m, const battle::Unit * target) const override
  247. {
  248. const bool battleWideNegation = target->hasBonusOfType(BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, 0);
  249. const bool heroNegation = target->hasBonusOfType(BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, 1);
  250. //Non-magical effects is not affected by orb of vulnerability
  251. if(!m->isMagicalEffect())
  252. return false;
  253. //anyone can cast on artifact holder`s stacks
  254. if(heroNegation)
  255. {
  256. return true;
  257. }
  258. //this stack is from other player
  259. else if(battleWideNegation)
  260. {
  261. if(m->ownerMatches(target, false))
  262. return true;
  263. }
  264. return false;
  265. }
  266. };
  267. class DefaultTargetConditionItemFactory : public TargetConditionItemFactory
  268. {
  269. public:
  270. Object createAbsoluteLevel() const override
  271. {
  272. static std::shared_ptr<TargetConditionItem> antimagicCondition = std::make_shared<AbsoluteLevelCondition>();
  273. return antimagicCondition;
  274. }
  275. Object createAbsoluteSpell() const override
  276. {
  277. static std::shared_ptr<TargetConditionItem> alCondition = std::make_shared<AbsoluteSpellCondition>();
  278. return alCondition;
  279. }
  280. Object createElemental() const override
  281. {
  282. static std::shared_ptr<TargetConditionItem> elementalCondition = std::make_shared<ElementalCondition>();
  283. return elementalCondition;
  284. }
  285. Object createResistance() const override
  286. {
  287. static auto elementalCondition = std::make_shared<ResistanceCondition>();
  288. return elementalCondition;
  289. }
  290. Object createNormalLevel() const override
  291. {
  292. static std::shared_ptr<TargetConditionItem> nlCondition = std::make_shared<NormalLevelCondition>();
  293. return nlCondition;
  294. }
  295. Object createNormalSpell() const override
  296. {
  297. static std::shared_ptr<TargetConditionItem> nsCondition = std::make_shared<NormalSpellCondition>();
  298. return nsCondition;
  299. }
  300. Object createConfigurable(std::string scope, std::string type, std::string identifier) const override
  301. {
  302. if(type == "bonus")
  303. {
  304. //TODO: support custom bonus types
  305. auto it = bonusNameMap.find(identifier);
  306. if(it != bonusNameMap.end())
  307. return std::make_shared<SelectorCondition>(Selector::type()(it->second));
  308. auto params = BonusParams(identifier, "", -1);
  309. if(params.isConverted)
  310. {
  311. if(params.val)
  312. return std::make_shared<SelectorCondition>(params.toSelector(), *params.val, *params.val);
  313. return std::make_shared<SelectorCondition>(params.toSelector());
  314. }
  315. logMod->error("Invalid bonus type %s in spell target condition.", identifier);
  316. }
  317. else if(type == "creature")
  318. {
  319. auto rawId = VLC->modh->identifiers.getIdentifier(scope, type, identifier, true);
  320. if(rawId)
  321. return std::make_shared<CreatureCondition>(CreatureID(rawId.value()));
  322. else
  323. logMod->error("Invalid creature %s type in spell target condition.", identifier);
  324. }
  325. else if(type == "spell")
  326. {
  327. auto rawId = VLC->modh->identifiers.getIdentifier(scope, type, identifier, true);
  328. if(rawId)
  329. return std::make_shared<SpellEffectCondition>(SpellID(rawId.value()));
  330. else
  331. logMod->error("Invalid spell %s in spell target condition.", identifier);
  332. }
  333. else if(type == "healthValueSpecial")
  334. {
  335. return std::make_shared<HealthValueCondition>();
  336. }
  337. else
  338. {
  339. logMod->error("Invalid type %s in spell target condition.", type);
  340. }
  341. return Object();
  342. }
  343. Object createFromJsonStruct(const JsonNode & jsonStruct) const override
  344. {
  345. auto type = jsonStruct["type"].String();
  346. auto parameters = jsonStruct["parameters"];
  347. if(type == "selector")
  348. {
  349. auto minVal = std::numeric_limits<si32>::min();
  350. auto maxVal = std::numeric_limits<si32>::max();
  351. if(parameters["minVal"].isNumber())
  352. minVal = parameters["minVal"].Integer();
  353. if(parameters["maxVal"].isNumber())
  354. maxVal = parameters["maxVal"].Integer();
  355. auto sel = JsonUtils::parseSelector(parameters);
  356. return std::make_shared<SelectorCondition>(sel, minVal, maxVal);
  357. }
  358. logMod->error("Invalid type %s in spell target condition.", type);
  359. return Object();
  360. }
  361. Object createReceptiveFeature() const override
  362. {
  363. static std::shared_ptr<TargetConditionItem> condition = std::make_shared<ReceptiveFeatureCondition>();
  364. return condition;
  365. }
  366. Object createImmunityNegation() const override
  367. {
  368. static std::shared_ptr<TargetConditionItem> condition = std::make_shared<ImmunityNegationCondition>();
  369. return condition;
  370. }
  371. };
  372. const TargetConditionItemFactory * TargetConditionItemFactory::getDefault()
  373. {
  374. static std::unique_ptr<TargetConditionItemFactory> singleton;
  375. if(!singleton)
  376. singleton = std::make_unique<DefaultTargetConditionItemFactory>();
  377. return singleton.get();
  378. }
  379. bool TargetCondition::isReceptive(const Mechanics * m, const battle::Unit * target) const
  380. {
  381. if(!check(absolute, m, target))
  382. return false;
  383. for(const auto & item : negation)
  384. {
  385. if(item->isReceptive(m, target))
  386. return true;
  387. }
  388. return check(normal, m, target);
  389. }
  390. void TargetCondition::serializeJson(JsonSerializeFormat & handler, const ItemFactory * itemFactory)
  391. {
  392. if(handler.saving)
  393. {
  394. logGlobal->error("Spell target condition saving is not supported");
  395. return;
  396. }
  397. absolute.clear();
  398. normal.clear();
  399. negation.clear();
  400. absolute.push_back(itemFactory->createAbsoluteSpell());
  401. absolute.push_back(itemFactory->createAbsoluteLevel());
  402. normal.push_back(itemFactory->createElemental());
  403. normal.push_back(itemFactory->createResistance());
  404. normal.push_back(itemFactory->createNormalLevel());
  405. normal.push_back(itemFactory->createNormalSpell());
  406. negation.push_back(itemFactory->createReceptiveFeature());
  407. negation.push_back(itemFactory->createImmunityNegation());
  408. {
  409. auto anyOf = handler.enterStruct("anyOf");
  410. loadConditions(anyOf->getCurrent(), false, false, itemFactory);
  411. }
  412. {
  413. auto allOf = handler.enterStruct("allOf");
  414. loadConditions(allOf->getCurrent(), true, false, itemFactory);
  415. }
  416. {
  417. auto noneOf = handler.enterStruct("noneOf");
  418. loadConditions(noneOf->getCurrent(), true, true, itemFactory);
  419. }
  420. }
  421. bool TargetCondition::check(const ItemVector & condition, const Mechanics * m, const battle::Unit * target) const
  422. {
  423. bool nonExclusiveCheck = false;
  424. bool nonExclusiveExits = false;
  425. for(const auto & item : condition)
  426. {
  427. if(item->isExclusive())
  428. {
  429. if(!item->isReceptive(m, target))
  430. return false;
  431. }
  432. else
  433. {
  434. if(item->isReceptive(m, target))
  435. nonExclusiveCheck = true;
  436. nonExclusiveExits = true;
  437. }
  438. }
  439. return !nonExclusiveExits || nonExclusiveCheck;
  440. }
  441. void TargetCondition::loadConditions(const JsonNode & source, bool exclusive, bool inverted, const ItemFactory * itemFactory)
  442. {
  443. for(const auto & keyValue : source.Struct())
  444. {
  445. bool isAbsolute;
  446. const JsonNode & value = keyValue.second;
  447. if(value.String() == "absolute")
  448. isAbsolute = true;
  449. else if(value.String() == "normal")
  450. isAbsolute = false;
  451. else if(value.isStruct()) //assume conditions have a new struct format
  452. isAbsolute = value["absolute"].Bool();
  453. else
  454. continue;
  455. std::shared_ptr<TargetConditionItem> item;
  456. if(value.isStruct())
  457. item = itemFactory->createFromJsonStruct(value);
  458. else
  459. {
  460. std::string scope;
  461. std::string type;
  462. std::string identifier;
  463. CModHandler::parseIdentifier(keyValue.first, scope, type, identifier);
  464. item = itemFactory->createConfigurable(scope, type, identifier);
  465. }
  466. if(item)
  467. {
  468. item->setExclusive(exclusive);
  469. item->setInverted(inverted);
  470. if(isAbsolute)
  471. absolute.push_back(item);
  472. else
  473. normal.push_back(item);
  474. }
  475. }
  476. }
  477. }
  478. VCMI_LIB_NAMESPACE_END