HeroBonus.cpp 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264
  1. #define VCMI_DLL
  2. #include "HeroBonus.h"
  3. #include <boost/foreach.hpp>
  4. #include "VCMI_Lib.h"
  5. #include "CSpellHandler.h"
  6. #include <sstream>
  7. #include "CCreatureHandler.h"
  8. #include <boost/assign/list_of.hpp>
  9. #include "CCreatureSet.h"
  10. #include <boost/algorithm/string/trim.hpp>
  11. #include <boost/bind.hpp>
  12. #include "CHeroHandler.h"
  13. #include "CGeneralTextHandler.h"
  14. #include "BattleState.h"
  15. #include "CArtHandler.h"
  16. #define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); BOOST_FOREACH(CBonusSystemNode *pname, lparents)
  17. #define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); BOOST_FOREACH(CBonusSystemNode *pname, lchildren)
  18. #define FOREACH_RED_PARENT(pname) TNodes lparents; getRedParents(lparents); BOOST_FOREACH(CBonusSystemNode *pname, lparents)
  19. #define BONUS_NAME(x) ( #x, Bonus::x )
  20. DLL_EXPORT const std::map<std::string, int> bonusNameMap = boost::assign::map_list_of BONUS_LIST;
  21. #undef BONUS_NAME
  22. #define BONUS_LOG_LINE(x) tlog5 << x << std::endl
  23. int CBonusSystemNode::treeChanged = 1;
  24. const bool CBonusSystemNode::cachingEnabled = true;
  25. BonusList::BonusList(bool BelongsToTree /* =false */) : belongsToTree(BelongsToTree)
  26. {
  27. }
  28. BonusList::BonusList(const BonusList &bonusList)
  29. {
  30. bonuses.resize(bonusList.size());
  31. std::copy(bonusList.begin(), bonusList.end(), bonuses.begin());
  32. belongsToTree = false;
  33. }
  34. BonusList& BonusList::operator=(const BonusList &bonusList)
  35. {
  36. bonuses.resize(bonusList.size());
  37. std::copy(bonusList.begin(), bonusList.end(), bonuses.begin());
  38. belongsToTree = false;
  39. return *this;
  40. }
  41. int BonusList::totalValue() const
  42. {
  43. int base = 0;
  44. int percentToBase = 0;
  45. int percentToAll = 0;
  46. int additive = 0;
  47. int indepMax = 0;
  48. bool hasIndepMax = false;
  49. int indepMin = 0;
  50. bool hasIndepMin = false;
  51. BOOST_FOREACH(Bonus *i, bonuses)
  52. {
  53. switch(i->valType)
  54. {
  55. case Bonus::BASE_NUMBER:
  56. base += i->val;
  57. break;
  58. case Bonus::PERCENT_TO_ALL:
  59. percentToAll += i->val;
  60. break;
  61. case Bonus::PERCENT_TO_BASE:
  62. percentToBase += i->val;
  63. break;
  64. case Bonus::ADDITIVE_VALUE:
  65. additive += i->val;
  66. break;
  67. case Bonus::INDEPENDENT_MAX:
  68. if (!hasIndepMax)
  69. {
  70. indepMax = i->val;
  71. hasIndepMax = true;
  72. }
  73. else
  74. {
  75. amax(indepMax, i->val);
  76. }
  77. break;
  78. case Bonus::INDEPENDENT_MIN:
  79. if (!hasIndepMin)
  80. {
  81. indepMin = i->val;
  82. hasIndepMin = true;
  83. }
  84. else
  85. {
  86. amin(indepMin, i->val);
  87. }
  88. break;
  89. }
  90. }
  91. int modifiedBase = base + (base * percentToBase) / 100;
  92. modifiedBase += additive;
  93. int valFirst = (modifiedBase * (100 + percentToAll)) / 100;
  94. if(hasIndepMin && hasIndepMax)
  95. assert(indepMin < indepMax);
  96. if (hasIndepMax)
  97. amax(valFirst, indepMax);
  98. if (hasIndepMin)
  99. amin(valFirst, indepMin);
  100. return valFirst;
  101. }
  102. const Bonus * BonusList::getFirst(const CSelector &selector) const
  103. {
  104. for (unsigned int i = 0; i < bonuses.size(); i++)
  105. {
  106. const Bonus *b = bonuses[i];
  107. if(selector(b))
  108. return &*b;
  109. }
  110. return NULL;
  111. }
  112. Bonus * BonusList::getFirst(const CSelector &select)
  113. {
  114. for (unsigned int i = 0; i < bonuses.size(); i++)
  115. {
  116. Bonus *b = bonuses[i];
  117. if(select(b))
  118. return &*b;
  119. }
  120. return NULL;
  121. }
  122. void BonusList::getModifiersWDescr(TModDescr &out) const
  123. {
  124. BOOST_FOREACH(Bonus *i, bonuses)
  125. out.push_back(std::make_pair(i->val, i->Description()));
  126. }
  127. void BonusList::getBonuses(boost::shared_ptr<BonusList> out, const CSelector &selector) const
  128. {
  129. // BOOST_FOREACH(Bonus *i, *this)
  130. // if(selector(i) && i->effectRange == Bonus::NO_LIMIT)
  131. // out.push_back(i);
  132. getBonuses(out, selector, 0);
  133. }
  134. void BonusList::getBonuses(boost::shared_ptr<BonusList> out, const CSelector &selector, const CSelector &limit, const bool caching /*= false*/) const
  135. {
  136. for (unsigned int i = 0; i < bonuses.size(); i++)
  137. {
  138. Bonus *b = bonuses[i];
  139. //add matching bonuses that matches limit predicate or have NO_LIMIT if no given predicate
  140. if(caching || (selector(b) && ((!limit && b->effectRange == Bonus::NO_LIMIT) || (limit && limit(b)))))
  141. out->push_back(b);
  142. }
  143. }
  144. int BonusList::valOfBonuses(const CSelector &select) const
  145. {
  146. boost::shared_ptr<BonusList> ret(new BonusList());
  147. CSelector limit = 0;
  148. getBonuses(ret, select, limit, false);
  149. ret->eliminateDuplicates();
  150. return ret->totalValue();
  151. }
  152. void BonusList::limit(const CBonusSystemNode &node)
  153. {
  154. remove_if(boost::bind(&CBonusSystemNode::isLimitedOnUs, boost::ref(node), _1));
  155. }
  156. void BonusList::eliminateDuplicates()
  157. {
  158. sort( bonuses.begin(), bonuses.end() );
  159. bonuses.erase( unique( bonuses.begin(), bonuses.end() ), bonuses.end() );
  160. }
  161. void BonusList::push_back(Bonus* const &x)
  162. {
  163. bonuses.push_back(x);
  164. if (belongsToTree)
  165. CBonusSystemNode::incrementTreeChangedNum();
  166. }
  167. std::vector<Bonus*>::iterator BonusList::erase(std::vector<Bonus*>::iterator position)
  168. {
  169. if (belongsToTree)
  170. CBonusSystemNode::incrementTreeChangedNum();
  171. return bonuses.erase(position);
  172. }
  173. void BonusList::clear()
  174. {
  175. bonuses.clear();
  176. if (belongsToTree)
  177. CBonusSystemNode::incrementTreeChangedNum();
  178. }
  179. std::vector<BonusList*>::size_type BonusList::operator-=(Bonus* const &i)
  180. {
  181. std::vector<Bonus*>::iterator itr = std::find(bonuses.begin(), bonuses.end(), i);
  182. if(itr == bonuses.end())
  183. return false;
  184. bonuses.erase(itr);
  185. if (belongsToTree)
  186. CBonusSystemNode::incrementTreeChangedNum();
  187. return true;
  188. }
  189. void BonusList::resize(std::vector<Bonus*>::size_type sz, Bonus* c )
  190. {
  191. bonuses.resize(sz, c);
  192. if (belongsToTree)
  193. CBonusSystemNode::incrementTreeChangedNum();
  194. }
  195. void BonusList::insert(std::vector<Bonus*>::iterator position, std::vector<Bonus*>::size_type n, Bonus* const &x)
  196. {
  197. bonuses.insert(position, n, x);
  198. if (belongsToTree)
  199. CBonusSystemNode::incrementTreeChangedNum();
  200. }
  201. int IBonusBearer::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const
  202. {
  203. return valOfBonuses(Selector::type(type) && selector);
  204. }
  205. int IBonusBearer::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) const
  206. {
  207. CSelector s = Selector::type(type);
  208. if(subtype != -1)
  209. s = s && Selector::subtype(subtype);
  210. return valOfBonuses(s);
  211. }
  212. int IBonusBearer::valOfBonuses(const CSelector &selector) const
  213. {
  214. CSelector limit = 0;
  215. boost::shared_ptr<BonusList> hlp = getAllBonuses(selector, limit, NULL);
  216. return hlp->totalValue();
  217. }
  218. bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachingStr /*= ""*/) const
  219. {
  220. return getBonuses(selector, cachingStr)->size() > 0;
  221. }
  222. bool IBonusBearer::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) const
  223. {
  224. CSelector s = Selector::type(type);
  225. std::string cachingStr = "";
  226. if(subtype != -1)
  227. s = s && Selector::subtype(subtype);
  228. else
  229. {
  230. cachingStr = "type_";
  231. cachingStr += ((char) type);
  232. }
  233. return hasBonus(s, cachingStr);
  234. }
  235. void IBonusBearer::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */) const
  236. {
  237. getModifiersWDescr(out, subtype != -1 ? Selector::typeSubtype(type, subtype) : Selector::type(type));
  238. }
  239. void IBonusBearer::getModifiersWDescr(TModDescr &out, const CSelector &selector) const
  240. {
  241. getBonuses(selector)->getModifiersWDescr(out);
  242. }
  243. int IBonusBearer::getBonusesCount(int from, int id) const
  244. {
  245. return getBonusesCount(Selector::source(from, id));
  246. }
  247. int IBonusBearer::getBonusesCount(const CSelector &selector) const
  248. {
  249. return getBonuses(selector)->size();
  250. }
  251. const boost::shared_ptr<BonusList> IBonusBearer::getBonuses(const CSelector &selector, const std::string &cachingStr /*= ""*/) const
  252. {
  253. return getAllBonuses(selector, 0, NULL, cachingStr);
  254. }
  255. const boost::shared_ptr<BonusList> IBonusBearer::getBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr /*= ""*/) const
  256. {
  257. return getAllBonuses(selector, limit, NULL, cachingStr);
  258. }
  259. bool IBonusBearer::hasBonusFrom(ui8 source, ui32 sourceID) const
  260. {
  261. return hasBonus(Selector::source(source,sourceID));
  262. }
  263. int IBonusBearer::MoraleVal() const
  264. {
  265. if(hasBonusOfType(Bonus::NON_LIVING) || hasBonusOfType(Bonus::UNDEAD) ||
  266. hasBonusOfType(Bonus::NO_MORALE) || hasBonusOfType(Bonus::SIEGE_WEAPON))
  267. return 0;
  268. int ret = valOfBonuses(Selector::type(Bonus::MORALE));
  269. if(hasBonusOfType(Bonus::SELF_MORALE)) //eg. minotaur
  270. amax(ret, +1);
  271. return abetw(ret, -3, +3);
  272. }
  273. int IBonusBearer::LuckVal() const
  274. {
  275. if(hasBonusOfType(Bonus::NO_LUCK))
  276. return 0;
  277. int ret = valOfBonuses(Selector::type(Bonus::LUCK));
  278. if(hasBonusOfType(Bonus::SELF_LUCK)) //eg. halfling
  279. amax(ret, +1);
  280. return abetw(ret, -3, +3);
  281. }
  282. si32 IBonusBearer::Attack() const
  283. {
  284. si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
  285. if(int frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker
  286. {
  287. ret += frenzyPower * Defense(false);
  288. }
  289. amax(ret, 0);
  290. return ret;
  291. }
  292. si32 IBonusBearer::Defense(bool withFrenzy /*= true*/) const
  293. {
  294. si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
  295. if(withFrenzy && hasBonusOfType(Bonus::IN_FRENZY)) //frenzy for defender
  296. {
  297. return 0;
  298. }
  299. amax(ret, 0);
  300. return ret;
  301. }
  302. ui16 IBonusBearer::MaxHealth() const
  303. {
  304. return std::max(1, valOfBonuses(Bonus::STACK_HEALTH)); //never 0 or negative
  305. }
  306. ui32 IBonusBearer::getMinDamage() const
  307. {
  308. return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1));
  309. }
  310. ui32 IBonusBearer::getMaxDamage() const
  311. {
  312. return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2));
  313. }
  314. si32 IBonusBearer::manaLimit() const
  315. {
  316. return si32(getPrimSkillLevel(3) * (100.0f + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 24)) / 10.0f);
  317. }
  318. int IBonusBearer::getPrimSkillLevel(int id) const
  319. {
  320. int ret = 0;
  321. if(id == PrimarySkill::ATTACK)
  322. ret = Attack();
  323. else if(id == PrimarySkill::DEFENSE)
  324. ret = Defense();
  325. else
  326. ret = valOfBonuses(Bonus::PRIMARY_SKILL, id);
  327. amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge
  328. return ret;
  329. }
  330. si32 IBonusBearer::magicResistance() const
  331. {
  332. return valOfBonuses(Selector::type(Bonus::MAGIC_RESISTANCE));
  333. }
  334. bool IBonusBearer::isLiving() const //TODO: theoreticaly there exists "LIVING" bonus in stack experience documentation
  335. {
  336. return(!hasBonus(Selector::type(Bonus::UNDEAD) || Selector::type(Bonus::NON_LIVING)));
  337. }
  338. const boost::shared_ptr<BonusList> IBonusBearer::getSpellBonuses() const
  339. {
  340. std::string cachingStr = "source_";
  341. cachingStr += (char) Bonus::SPELL_EFFECT;
  342. return getBonuses(Selector::sourceType(Bonus::SPELL_EFFECT), cachingStr);
  343. }
  344. Bonus * CBonusSystemNode::getBonus(const CSelector &selector)
  345. {
  346. Bonus *ret = bonuses.getFirst(selector);
  347. if(ret)
  348. return ret;
  349. FOREACH_PARENT(pname)
  350. {
  351. ret = pname->getBonus(selector);
  352. if (ret)
  353. return ret;
  354. }
  355. return NULL;
  356. }
  357. const Bonus * CBonusSystemNode::getBonus( const CSelector &selector ) const
  358. {
  359. return (const_cast<CBonusSystemNode*>(this))->getBonus(selector);
  360. }
  361. void CBonusSystemNode::getParents(TCNodes &out) const /*retreives list of parent nodes (nodes to inherit bonuses from) */
  362. {
  363. for (unsigned int i = 0; i < parents.size(); i++)
  364. {
  365. const CBonusSystemNode *parent = parents[i];
  366. out.insert(parent);
  367. }
  368. }
  369. void CBonusSystemNode::getParents(TNodes &out)
  370. {
  371. for (unsigned int i = 0; i < parents.size(); i++)
  372. {
  373. const CBonusSystemNode *parent = parents[i];
  374. out.insert(const_cast<CBonusSystemNode*>(parent));
  375. }
  376. }
  377. void CBonusSystemNode::getAllBonusesRec(boost::shared_ptr<BonusList> out, const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/, const bool caching /*= false*/) const
  378. {
  379. TCNodes lparents;
  380. getParents(lparents);
  381. BOOST_FOREACH(const CBonusSystemNode *p, lparents)
  382. p->getAllBonusesRec(out, selector, limit, root ? root : this, caching);
  383. bonuses.getBonuses(out, selector, limit, caching);
  384. if(!root)
  385. out->limit(*this);
  386. }
  387. const boost::shared_ptr<BonusList> CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/, const std::string &cachingStr /*= ""*/) const
  388. {
  389. boost::shared_ptr<BonusList> ret(new BonusList());
  390. if (CBonusSystemNode::cachingEnabled)
  391. {
  392. static boost::mutex m;
  393. boost::mutex::scoped_lock lock(m);
  394. if (cachedLast != treeChanged)
  395. {
  396. getAllBonusesRec(ret, selector, limit, this, true);
  397. ret->eliminateDuplicates();
  398. cachedBonuses = *ret;
  399. ret->clear();
  400. cachedRequests.clear();
  401. cachedLast = treeChanged;
  402. }
  403. if (cachingStr != "")
  404. {
  405. std::map<std::string, boost::shared_ptr<BonusList> >::iterator it(cachedRequests.find(cachingStr));
  406. if (cachedRequests.size() > 0 && it != cachedRequests.end())
  407. {
  408. ret = it->second;
  409. return ret;
  410. }
  411. }
  412. cachedBonuses.getBonuses(ret, selector, limit, false);
  413. if (!root)
  414. ret->limit(*this);
  415. if (cachingStr != "")
  416. cachedRequests[cachingStr] = ret;
  417. return ret;
  418. }
  419. else
  420. {
  421. getAllBonusesRec(ret, selector, limit, root, false);
  422. ret->eliminateDuplicates();
  423. return ret;
  424. }
  425. }
  426. CBonusSystemNode::CBonusSystemNode() : bonuses(true), exportedBonuses(true), nodeType(UNKNOWN), cachedLast(0)
  427. {
  428. }
  429. CBonusSystemNode::~CBonusSystemNode()
  430. {
  431. detachFromAll();
  432. if(children.size())
  433. {
  434. tlog2 << "Warning: an orphaned child!\n";
  435. while(children.size())
  436. children.front()->detachFrom(this);
  437. }
  438. BOOST_FOREACH(Bonus *b, exportedBonuses)
  439. delete b;
  440. }
  441. void CBonusSystemNode::attachTo(CBonusSystemNode *parent)
  442. {
  443. assert(!vstd::contains(parents, parent));
  444. parents.push_back(parent);
  445. if(parent->actsAsBonusSourceOnly())
  446. parent->newRedDescendant(this);
  447. else
  448. newRedDescendant(parent);
  449. parent->newChildAttached(this);
  450. CBonusSystemNode::treeChanged++;
  451. }
  452. void CBonusSystemNode::detachFrom(CBonusSystemNode *parent)
  453. {
  454. assert(vstd::contains(parents, parent));
  455. if(parent->actsAsBonusSourceOnly())
  456. parent->removedRedDescendant(this);
  457. else
  458. removedRedDescendant(parent);
  459. parents -= parent;
  460. parent->childDetached(this);
  461. CBonusSystemNode::treeChanged++;
  462. }
  463. void CBonusSystemNode::popBonuses(const CSelector &s)
  464. {
  465. boost::shared_ptr<BonusList> bl(new BonusList);
  466. exportedBonuses.getBonuses(bl, s);
  467. BOOST_FOREACH(Bonus *b, *bl)
  468. removeBonus(b);
  469. BOOST_FOREACH(CBonusSystemNode *child, children)
  470. child->popBonuses(s);
  471. }
  472. // void CBonusSystemNode::addNewBonus(const Bonus &b)
  473. // {
  474. // addNewBonus(new Bonus(b));
  475. // }
  476. void CBonusSystemNode::addNewBonus(Bonus *b)
  477. {
  478. assert(!vstd::contains(exportedBonuses,b));
  479. exportedBonuses.push_back(b);
  480. exportBonus(b);
  481. CBonusSystemNode::treeChanged++;
  482. }
  483. void CBonusSystemNode::removeBonus(Bonus *b)
  484. {
  485. exportedBonuses -= b;
  486. if(b->propagator)
  487. unpropagateBonus(b);
  488. else
  489. bonuses -= b;
  490. delNull(b);
  491. CBonusSystemNode::treeChanged++;
  492. }
  493. bool CBonusSystemNode::isLimitedOnUs(Bonus *b) const
  494. {
  495. return b->limiter && b->limiter->limit(b, *this);
  496. }
  497. bool CBonusSystemNode::actsAsBonusSourceOnly() const
  498. {
  499. switch(nodeType)
  500. {
  501. case CREATURE:
  502. case ARTIFACT:
  503. case ARTIFACT_INSTANCE:
  504. return true;
  505. default:
  506. return false;
  507. }
  508. }
  509. void CBonusSystemNode::propagateBonus(Bonus * b)
  510. {
  511. if(b->propagator->shouldBeAttached(this))
  512. {
  513. bonuses.push_back(b);
  514. BONUS_LOG_LINE("#$# " << b->Description() << " #propagated to# " << nodeName());
  515. }
  516. FOREACH_RED_CHILD(child)
  517. child->propagateBonus(b);
  518. }
  519. void CBonusSystemNode::unpropagateBonus(Bonus * b)
  520. {
  521. if(b->propagator->shouldBeAttached(this))
  522. {
  523. bonuses -= b;
  524. BONUS_LOG_LINE("#$#" << b->Description() << " #is no longer propagated to# " << nodeName());
  525. }
  526. FOREACH_RED_CHILD(child)
  527. child->unpropagateBonus(b);
  528. }
  529. void CBonusSystemNode::newChildAttached(CBonusSystemNode *child)
  530. {
  531. assert(!vstd::contains(children, child));
  532. children.push_back(child);
  533. BONUS_LOG_LINE(child->nodeName() << " #attached to# " << nodeName());
  534. }
  535. void CBonusSystemNode::childDetached(CBonusSystemNode *child)
  536. {
  537. assert(vstd::contains(children, child));
  538. children -= child;
  539. BONUS_LOG_LINE(child->nodeName() << " #detached from# " << nodeName());
  540. }
  541. void CBonusSystemNode::detachFromAll()
  542. {
  543. while(parents.size())
  544. detachFrom(parents.front());
  545. }
  546. bool CBonusSystemNode::isIndependentNode() const
  547. {
  548. return parents.empty() && children.empty();
  549. }
  550. std::string CBonusSystemNode::nodeName() const
  551. {
  552. return description.size()
  553. ? description
  554. : std::string("Bonus system node of type ") + typeid(*this).name();
  555. }
  556. void CBonusSystemNode::deserializationFix()
  557. {
  558. exportBonuses();
  559. }
  560. void CBonusSystemNode::getRedParents(TNodes &out)
  561. {
  562. FOREACH_PARENT(pname)
  563. {
  564. if(pname->actsAsBonusSourceOnly())
  565. {
  566. out.insert(pname);
  567. }
  568. }
  569. if(!actsAsBonusSourceOnly())
  570. {
  571. BOOST_FOREACH(CBonusSystemNode *child, children)
  572. {
  573. out.insert(child);
  574. }
  575. }
  576. }
  577. void CBonusSystemNode::getRedChildren(TNodes &out)
  578. {
  579. FOREACH_PARENT(pname)
  580. {
  581. if(!pname->actsAsBonusSourceOnly())
  582. {
  583. out.insert(pname);
  584. }
  585. }
  586. if(actsAsBonusSourceOnly())
  587. {
  588. BOOST_FOREACH(CBonusSystemNode *child, children)
  589. {
  590. out.insert(child);
  591. }
  592. }
  593. }
  594. void CBonusSystemNode::newRedDescendant(CBonusSystemNode *descendant)
  595. {
  596. BOOST_FOREACH(Bonus *b, exportedBonuses)
  597. if(b->propagator)
  598. descendant->propagateBonus(b);
  599. FOREACH_RED_PARENT(parent)
  600. parent->newRedDescendant(descendant);
  601. }
  602. void CBonusSystemNode::removedRedDescendant(CBonusSystemNode *descendant)
  603. {
  604. BOOST_FOREACH(Bonus *b, exportedBonuses)
  605. if(b->propagator)
  606. descendant->unpropagateBonus(b);
  607. FOREACH_RED_PARENT(parent)
  608. parent->removedRedDescendant(descendant);
  609. }
  610. void CBonusSystemNode::getRedAncestors(TNodes &out)
  611. {
  612. getRedParents(out);
  613. FOREACH_RED_PARENT(p)
  614. p->getRedAncestors(out);
  615. }
  616. void CBonusSystemNode::getRedDescendants(TNodes &out)
  617. {
  618. getRedChildren(out);
  619. FOREACH_RED_CHILD(c)
  620. c->getRedChildren(out);
  621. }
  622. void CBonusSystemNode::battleTurnPassed()
  623. {
  624. BonusList bonusesCpy = exportedBonuses; //copy, because removing bonuses invalidates iters
  625. for (unsigned int i = 0; i < bonusesCpy.size(); i++)
  626. {
  627. Bonus *b = bonusesCpy[i];
  628. if(b->duration & Bonus::N_TURNS)
  629. {
  630. b->turnsRemain--;
  631. if(b->turnsRemain <= 0)
  632. removeBonus(b);
  633. }
  634. }
  635. }
  636. void CBonusSystemNode::exportBonus(Bonus * b)
  637. {
  638. if(b->propagator)
  639. propagateBonus(b);
  640. else
  641. bonuses.push_back(b);
  642. CBonusSystemNode::treeChanged++;
  643. }
  644. void CBonusSystemNode::exportBonuses()
  645. {
  646. BOOST_FOREACH(Bonus *b, exportedBonuses)
  647. exportBonus(b);
  648. }
  649. const ui8 CBonusSystemNode::getNodeType() const
  650. {
  651. return nodeType;
  652. }
  653. BonusList& CBonusSystemNode::getBonusList()
  654. {
  655. return bonuses;
  656. }
  657. const BonusList& CBonusSystemNode::getBonusList() const
  658. {
  659. return bonuses;
  660. }
  661. const TNodesVector& CBonusSystemNode::getParentNodes() const
  662. {
  663. return parents;
  664. }
  665. const TNodesVector& CBonusSystemNode::getChildrenNodes() const
  666. {
  667. return children;
  668. }
  669. void CBonusSystemNode::setNodeType(ui8 type)
  670. {
  671. nodeType = type;
  672. }
  673. BonusList& CBonusSystemNode::getExportedBonusList()
  674. {
  675. return exportedBonuses;
  676. }
  677. const std::string& CBonusSystemNode::getDescription() const
  678. {
  679. return description;
  680. }
  681. void CBonusSystemNode::setDescription(const std::string &description)
  682. {
  683. this->description = description;
  684. }
  685. void CBonusSystemNode::incrementTreeChangedNum()
  686. {
  687. treeChanged++;
  688. }
  689. int NBonus::valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype /*= -1*/)
  690. {
  691. if(obj)
  692. return obj->valOfBonuses(type, subtype);
  693. return 0;
  694. }
  695. bool NBonus::hasOfType(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype /*= -1*/)
  696. {
  697. if(obj)
  698. return obj->hasBonusOfType(type, subtype);
  699. return false;
  700. }
  701. void NBonus::getModifiersWDescr(const CBonusSystemNode *obj, TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */)
  702. {
  703. if(obj)
  704. return obj->getModifiersWDescr(out, type, subtype);
  705. }
  706. int NBonus::getCount(const CBonusSystemNode *obj, int from, int id)
  707. {
  708. if(obj)
  709. return obj->getBonusesCount(from, id);
  710. return 0;
  711. }
  712. const CSpell * Bonus::sourceSpell() const
  713. {
  714. if(source == SPELL_EFFECT)
  715. return VLC->spellh->spells[sid];
  716. return NULL;
  717. }
  718. std::string Bonus::Description() const
  719. {
  720. if(description.size())
  721. return description;
  722. std::ostringstream str;
  723. str << std::showpos << val << " ";
  724. switch(source)
  725. {
  726. case ARTIFACT:
  727. str << VLC->arth->artifacts[sid]->Name();
  728. break;;
  729. case SPELL_EFFECT:
  730. str << VLC->spellh->spells[sid]->name;
  731. break;
  732. case CREATURE_ABILITY:
  733. str << VLC->creh->creatures[sid]->namePl;
  734. break;
  735. case SECONDARY_SKILL:
  736. str << VLC->generaltexth->skillName[sid]/* << " secondary skill"*/;
  737. break;
  738. }
  739. return str.str();
  740. }
  741. Bonus::Bonus(ui16 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype/*=-1*/)
  742. : duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), description(Desc)
  743. {
  744. additionalInfo = -1;
  745. turnsRemain = 0;
  746. valType = ADDITIVE_VALUE;
  747. effectRange = NO_LIMIT;
  748. boost::algorithm::trim(description);
  749. }
  750. Bonus::Bonus(ui16 Dur, ui8 Type, ui8 Src, si32 Val, ui32 ID, si32 Subtype/*=-1*/, ui8 ValType /*= ADDITIVE_VALUE*/)
  751. : duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), valType(ValType)
  752. {
  753. additionalInfo = -1;
  754. turnsRemain = 0;
  755. effectRange = NO_LIMIT;
  756. }
  757. Bonus::Bonus()
  758. {
  759. subtype = -1;
  760. additionalInfo = -1;
  761. turnsRemain = 0;
  762. valType = ADDITIVE_VALUE;
  763. effectRange = NO_LIMIT;
  764. }
  765. Bonus::~Bonus()
  766. {
  767. }
  768. Bonus * Bonus::addLimiter(ILimiter *Limiter)
  769. {
  770. return addLimiter(boost::shared_ptr<ILimiter>(Limiter));
  771. }
  772. Bonus * Bonus::addLimiter(boost::shared_ptr<ILimiter> Limiter)
  773. {
  774. limiter = Limiter;
  775. return this;
  776. }
  777. Bonus * Bonus::addPropagator(IPropagator *Propagator)
  778. {
  779. return addPropagator(boost::shared_ptr<IPropagator>(Propagator));
  780. }
  781. Bonus * Bonus::addPropagator(boost::shared_ptr<IPropagator> Propagator)
  782. {
  783. propagator = Propagator;
  784. return this;
  785. }
  786. CSelector DLL_EXPORT operator&&(const CSelector &first, const CSelector &second)
  787. {
  788. return CSelectorsConjunction(first, second);
  789. }
  790. CSelector DLL_EXPORT operator||(const CSelector &first, const CSelector &second)
  791. {
  792. return CSelectorsAlternative(first, second);
  793. }
  794. namespace Selector
  795. {
  796. DLL_EXPORT CSelectFieldEqual<TBonusType> type(&Bonus::type, 0);
  797. DLL_EXPORT CSelectFieldEqual<TBonusSubtype> subtype(&Bonus::subtype, 0);
  798. DLL_EXPORT CSelectFieldEqual<si32> info(&Bonus::additionalInfo, 0);
  799. DLL_EXPORT CSelectFieldEqual<ui16> duration(&Bonus::duration, 0);
  800. DLL_EXPORT CSelectFieldEqual<ui8> sourceType(&Bonus::source, 0);
  801. DLL_EXPORT CSelectFieldEqual<ui8> effectRange(&Bonus::effectRange, Bonus::NO_LIMIT);
  802. DLL_EXPORT CWillLastTurns turns;
  803. CSelector DLL_EXPORT typeSubtype(TBonusType Type, TBonusSubtype Subtype)
  804. {
  805. return type(Type) && subtype(Subtype);
  806. }
  807. CSelector DLL_EXPORT typeSubtypeInfo(TBonusType type, TBonusSubtype subtype, si32 info)
  808. {
  809. return CSelectFieldEqual<TBonusType>(&Bonus::type, type) && CSelectFieldEqual<TBonusSubtype>(&Bonus::subtype, subtype) && CSelectFieldEqual<si32>(&Bonus::additionalInfo, info);
  810. }
  811. CSelector DLL_EXPORT source(ui8 source, ui32 sourceID)
  812. {
  813. return CSelectFieldEqual<ui8>(&Bonus::source, source) && CSelectFieldEqual<ui32>(&Bonus::sid, sourceID);
  814. }
  815. CSelector DLL_EXPORT durationType(ui16 duration)
  816. {
  817. return CSelectFieldEqual<ui16>(&Bonus::duration, duration);
  818. }
  819. CSelector DLL_EXPORT sourceTypeSel(ui8 source)
  820. {
  821. return CSelectFieldEqual<ui8>(&Bonus::source, source);
  822. }
  823. bool DLL_EXPORT matchesType(const CSelector &sel, TBonusType type)
  824. {
  825. Bonus dummy;
  826. dummy.type = type;
  827. return sel(&dummy);
  828. }
  829. bool DLL_EXPORT matchesTypeSubtype(const CSelector &sel, TBonusType type, TBonusSubtype subtype)
  830. {
  831. Bonus dummy;
  832. dummy.type = type;
  833. dummy.subtype = subtype;
  834. return sel(&dummy);
  835. }
  836. bool DLL_EXPORT positiveSpellEffects(const Bonus *b)
  837. {
  838. if(b->source == Bonus::SPELL_EFFECT)
  839. {
  840. CSpell *sp = VLC->spellh->spells[b->sid];
  841. return sp->positiveness == 1;
  842. }
  843. return false; //not a spell effect
  844. }
  845. }
  846. const CStack * retreiveStackBattle(const CBonusSystemNode *node)
  847. {
  848. switch(node->getNodeType())
  849. {
  850. case CBonusSystemNode::STACK_BATTLE:
  851. return static_cast<const CStack*>(node);
  852. default:
  853. return NULL;
  854. }
  855. }
  856. const CStackInstance * retreiveStackInstance(const CBonusSystemNode *node)
  857. {
  858. switch(node->getNodeType())
  859. {
  860. case CBonusSystemNode::STACK_INSTANCE:
  861. return (static_cast<const CStackInstance *>(node));
  862. case CBonusSystemNode::STACK_BATTLE:
  863. return (static_cast<const CStack*>(node))->base;
  864. default:
  865. return NULL;
  866. }
  867. }
  868. const CCreature * retrieveCreature(const CBonusSystemNode *node)
  869. {
  870. switch(node->getNodeType())
  871. {
  872. case CBonusSystemNode::CREATURE:
  873. return (static_cast<const CCreature *>(node));
  874. default:
  875. const CStackInstance *csi = retreiveStackInstance(node);
  876. if(csi)
  877. return csi->type;
  878. return NULL;
  879. }
  880. }
  881. DLL_EXPORT std::ostream & operator<<(std::ostream &out, const BonusList &bonusList)
  882. {
  883. for (unsigned int i = 0; i < bonusList.size(); i++)
  884. {
  885. Bonus *b = bonusList[i];
  886. out << "Bonus " << i << "\n" << *b << std::endl;
  887. }
  888. return out;
  889. }
  890. DLL_EXPORT std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
  891. {
  892. for(std::map<std::string, int>::const_iterator i = bonusNameMap.begin(); i != bonusNameMap.end(); i++)
  893. if(i->second == bonus.type)
  894. out << "\tType: " << i->first << " \t";
  895. #define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
  896. printField(val);
  897. printField(subtype);
  898. printField(duration);
  899. printField(source);
  900. printField(sid);
  901. printField(additionalInfo);
  902. printField(turnsRemain);
  903. printField(valType);
  904. printField(effectRange);
  905. #undef printField
  906. return out;
  907. }
  908. ILimiter::~ILimiter()
  909. {
  910. }
  911. bool ILimiter::limit(const Bonus *b, const CBonusSystemNode &node) const /*return true to drop the bonus */
  912. {
  913. return false;
  914. }
  915. bool CCreatureTypeLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
  916. {
  917. const CCreature *c = retrieveCreature(&node);
  918. if(!c)
  919. return true;
  920. return c != creature && (!includeUpgrades || !creature->isMyUpgrade(c));
  921. //drop bonus if it's not our creature and (we dont check upgrades or its not our upgrade)
  922. }
  923. CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature &Creature, ui8 IncludeUpgrades /*= true*/)
  924. :creature(&Creature), includeUpgrades(IncludeUpgrades)
  925. {
  926. }
  927. CCreatureTypeLimiter::CCreatureTypeLimiter()
  928. {
  929. creature = NULL;
  930. includeUpgrades = false;
  931. }
  932. HasAnotherBonusLimiter::HasAnotherBonusLimiter( TBonusType bonus )
  933. : type(bonus), subtype(0), isSubtypeRelevant(false)
  934. {
  935. }
  936. HasAnotherBonusLimiter::HasAnotherBonusLimiter( TBonusType bonus, TBonusSubtype _subtype )
  937. : type(bonus), subtype(_subtype), isSubtypeRelevant(true)
  938. {
  939. }
  940. bool HasAnotherBonusLimiter::limit( const Bonus *b, const CBonusSystemNode &node ) const
  941. {
  942. if(isSubtypeRelevant)
  943. {
  944. return !node.hasBonusOfType(static_cast<Bonus::BonusType>(type), subtype);
  945. }
  946. else
  947. {
  948. return !node.hasBonusOfType(static_cast<Bonus::BonusType>(type));
  949. }
  950. }
  951. IPropagator::~IPropagator()
  952. {
  953. }
  954. // CBonusSystemNode * IPropagator::getDestNode(CBonusSystemNode *source, CBonusSystemNode *redParent, CBonusSystemNode *redChild)
  955. // {
  956. // tlog1 << "IPropagator::getDestNode called!\n";
  957. // return source;
  958. // }
  959. bool IPropagator::shouldBeAttached(CBonusSystemNode *dest)
  960. {
  961. return false;
  962. }
  963. // CBonusSystemNode * CPropagatorNodeType::getDestNode(CBonusSystemNode *source, CBonusSystemNode *redParent, CBonusSystemNode *redChild)
  964. // {
  965. // return NULL;
  966. // }
  967. CPropagatorNodeType::CPropagatorNodeType()
  968. {
  969. }
  970. CPropagatorNodeType::CPropagatorNodeType(ui8 NodeType)
  971. : nodeType(NodeType)
  972. {
  973. }
  974. bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
  975. {
  976. return nodeType == dest->getNodeType();
  977. }
  978. CreatureNativeTerrainLimiter::CreatureNativeTerrainLimiter(int TerrainType)
  979. : terrainType(TerrainType)
  980. {
  981. }
  982. CreatureNativeTerrainLimiter::CreatureNativeTerrainLimiter()
  983. {
  984. }
  985. bool CreatureNativeTerrainLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
  986. {
  987. const CCreature *c = retrieveCreature(&node);
  988. return !c || !iswith(c->faction, 0, 9) || VLC->heroh->nativeTerrains[c->faction] != terrainType; //drop bonus for non-creatures or non-native residents
  989. //TODO neutral creatues
  990. }
  991. CreatureFactionLimiter::CreatureFactionLimiter(int Faction)
  992. : faction(Faction)
  993. {
  994. }
  995. CreatureFactionLimiter::CreatureFactionLimiter()
  996. {
  997. }
  998. bool CreatureFactionLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
  999. {
  1000. const CCreature *c = retrieveCreature(&node);
  1001. return !c || c->faction != faction; //drop bonus for non-creatures or non-native residents
  1002. }
  1003. CreatureAlignmentLimiter::CreatureAlignmentLimiter()
  1004. {
  1005. }
  1006. CreatureAlignmentLimiter::CreatureAlignmentLimiter(si8 Alignment)
  1007. : alignment(Alignment)
  1008. {
  1009. }
  1010. bool CreatureAlignmentLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
  1011. {
  1012. const CCreature *c = retrieveCreature(&node);
  1013. if(!c)
  1014. return true;
  1015. switch(alignment)
  1016. {
  1017. case GOOD:
  1018. return !c->isGood(); //if not good -> return true (drop bonus)
  1019. case NEUTRAL:
  1020. return c->isEvil() || c->isGood();
  1021. case EVIL:
  1022. return !c->isEvil();
  1023. default:
  1024. tlog1 << "Warning: illegal alignment in limiter!\n";
  1025. return true;
  1026. }
  1027. }
  1028. RankRangeLimiter::RankRangeLimiter(ui8 Min, ui8 Max)
  1029. :minRank(Min), maxRank(Max)
  1030. {
  1031. }
  1032. RankRangeLimiter::RankRangeLimiter()
  1033. {
  1034. minRank = maxRank = -1;
  1035. }
  1036. bool RankRangeLimiter::limit( const Bonus *b, const CBonusSystemNode &node ) const
  1037. {
  1038. const CStackInstance *csi = retreiveStackInstance(&node);
  1039. if(csi)
  1040. return csi->getExpRank() < minRank || csi->getExpRank() > maxRank;
  1041. return true;
  1042. }
  1043. bool StackOwnerLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const
  1044. {
  1045. const CStack *s = retreiveStackBattle(&node);
  1046. if(s)
  1047. return s->owner != owner;
  1048. const CStackInstance *csi = retreiveStackInstance(&node);
  1049. if(csi && csi->armyObj)
  1050. return csi->armyObj->tempOwner != owner;
  1051. return true;
  1052. }
  1053. StackOwnerLimiter::StackOwnerLimiter()
  1054. : owner(-1)
  1055. {
  1056. }
  1057. StackOwnerLimiter::StackOwnerLimiter(ui8 Owner)
  1058. : owner(Owner)
  1059. {
  1060. }