TargetCondition.cpp 13 KB

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