HeroBonus.cpp 32 KB

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