HeroBonus.cpp 32 KB

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