CCreatureSet.cpp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. #include "StdInc.h"
  2. #include "CCreatureSet.h"
  3. #include "CCreatureHandler.h"
  4. #include "VCMI_Lib.h"
  5. #include "CModHandler.h"
  6. #include "CObjectHandler.h"
  7. #include "IGameCallback.h"
  8. #include "CGameState.h"
  9. #include "CGeneralTextHandler.h"
  10. #include "CSpellHandler.h"
  11. #include "CHeroHandler.h"
  12. /*
  13. * CCreatureSet.cpp, part of VCMI engine
  14. *
  15. * Authors: listed in file AUTHORS in main folder
  16. *
  17. * License: GNU General Public License v2.0 or later
  18. * Full text of license available in license.txt file, in main folder
  19. *
  20. */
  21. const CStackInstance &CCreatureSet::operator[](SlotID slot) const
  22. {
  23. TSlots::const_iterator i = stacks.find(slot);
  24. if (i != stacks.end())
  25. return *i->second;
  26. else
  27. throw std::runtime_error("That slot is empty!");
  28. }
  29. const CCreature* CCreatureSet::getCreature(SlotID slot) const
  30. {
  31. TSlots::const_iterator i = stacks.find(slot);
  32. if (i != stacks.end())
  33. return i->second->type;
  34. else
  35. return NULL;
  36. }
  37. bool CCreatureSet::setCreature(SlotID slot, CreatureID type, TQuantity quantity) /*slots 0 to 6 */
  38. {
  39. if(!slot.validSlot())
  40. {
  41. tlog1 << "Cannot set slot " << slot << std::endl;
  42. return false;
  43. }
  44. if(!quantity)
  45. {
  46. tlog2 << "Using set creature to delete stack?\n";
  47. eraseStack(slot);
  48. return true;
  49. }
  50. if(hasStackAtSlot(slot)) //remove old creature
  51. eraseStack(slot);
  52. putStack(slot, new CStackInstance(type, quantity));
  53. return true;
  54. }
  55. SlotID CCreatureSet::getSlotFor(CreatureID creature, ui32 slotsAmount/*=7*/) const /*returns -1 if no slot available */
  56. {
  57. return getSlotFor(VLC->creh->creatures[creature], slotsAmount);
  58. }
  59. SlotID CCreatureSet::getSlotFor(const CCreature *c, ui32 slotsAmount/*=ARMY_SIZE*/) const
  60. {
  61. assert(c->valid());
  62. for(TSlots::const_iterator i=stacks.begin(); i!=stacks.end(); ++i)
  63. {
  64. assert(i->second->type->valid());
  65. if(i->second->type == c)
  66. {
  67. return i->first; //if there is already such creature we return its slot id
  68. }
  69. }
  70. return getFreeSlot(slotsAmount);
  71. }
  72. SlotID CCreatureSet::getFreeSlot(ui32 slotsAmount/*=ARMY_SIZE*/) const
  73. {
  74. for(ui32 i=0; i<slotsAmount; i++)
  75. {
  76. if(!vstd::contains(stacks, SlotID(i)))
  77. {
  78. return SlotID(i); //return first free slot
  79. }
  80. }
  81. return SlotID(); //no slot available
  82. }
  83. int CCreatureSet::getStackCount(SlotID slot) const
  84. {
  85. TSlots::const_iterator i = stacks.find(slot);
  86. if (i != stacks.end())
  87. return i->second->count;
  88. else
  89. return 0; //TODO? consider issuing a warning
  90. }
  91. TExpType CCreatureSet::getStackExperience(SlotID slot) const
  92. {
  93. TSlots::const_iterator i = stacks.find(slot);
  94. if (i != stacks.end())
  95. return i->second->experience;
  96. else
  97. return 0; //TODO? consider issuing a warning
  98. }
  99. bool CCreatureSet::mergableStacks(std::pair<SlotID, SlotID> &out, SlotID preferable /*= -1*/) const /*looks for two same stacks, returns slot positions */
  100. {
  101. //try to match creature to our preferred stack
  102. if(preferable.validSlot() && vstd::contains(stacks, preferable))
  103. {
  104. const CCreature *cr = stacks.find(preferable)->second->type;
  105. for(TSlots::const_iterator j=stacks.begin(); j!=stacks.end(); ++j)
  106. {
  107. if(cr == j->second->type && j->first != preferable)
  108. {
  109. out.first = preferable;
  110. out.second = j->first;
  111. return true;
  112. }
  113. }
  114. }
  115. for(TSlots::const_iterator i=stacks.begin(); i!=stacks.end(); ++i)
  116. {
  117. for(TSlots::const_iterator j=stacks.begin(); j!=stacks.end(); ++j)
  118. {
  119. if(i->second->type == j->second->type && i->first != j->first)
  120. {
  121. out.first = i->first;
  122. out.second = j->first;
  123. return true;
  124. }
  125. }
  126. }
  127. return false;
  128. }
  129. void CCreatureSet::sweep()
  130. {
  131. for(TSlots::iterator i=stacks.begin(); i!=stacks.end(); ++i)
  132. {
  133. if(!i->second->count)
  134. {
  135. stacks.erase(i);
  136. sweep();
  137. break;
  138. }
  139. }
  140. }
  141. void CCreatureSet::addToSlot(SlotID slot, CreatureID cre, TQuantity count, bool allowMerging/* = true*/)
  142. {
  143. const CCreature *c = VLC->creh->creatures[cre];
  144. if(!hasStackAtSlot(slot))
  145. {
  146. setCreature(slot, cre, count);
  147. }
  148. else if(getCreature(slot) == c && allowMerging) //that slot was empty or contained same type creature
  149. {
  150. setStackCount(slot, getStackCount(slot) + count);
  151. }
  152. else
  153. {
  154. tlog1 << "Failed adding to slot!\n";
  155. }
  156. }
  157. void CCreatureSet::addToSlot(SlotID slot, CStackInstance *stack, bool allowMerging/* = true*/)
  158. {
  159. assert(stack->valid(true));
  160. if(!hasStackAtSlot(slot))
  161. {
  162. putStack(slot, stack);
  163. }
  164. else if(allowMerging && stack->type == getCreature(slot))
  165. {
  166. joinStack(slot, stack);
  167. }
  168. else
  169. {
  170. tlog1 << "Cannot add to slot " << slot << " stack " << *stack << std::endl;
  171. }
  172. }
  173. bool CCreatureSet::validTypes(bool allowUnrandomized /*= false*/) const
  174. {
  175. for(TSlots::const_iterator i=stacks.begin(); i!=stacks.end(); ++i)
  176. {
  177. if(!i->second->valid(allowUnrandomized))
  178. return false;
  179. }
  180. return true;
  181. }
  182. bool CCreatureSet::slotEmpty(SlotID slot) const
  183. {
  184. return !hasStackAtSlot(slot);
  185. }
  186. bool CCreatureSet::needsLastStack() const
  187. {
  188. return false;
  189. }
  190. ui64 CCreatureSet::getArmyStrength() const
  191. {
  192. ui64 ret = 0;
  193. for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); i++)
  194. ret += i->second->getPower();
  195. return ret;
  196. }
  197. ui64 CCreatureSet::getPower (SlotID slot) const
  198. {
  199. return getStack(slot).getPower();
  200. }
  201. std::string CCreatureSet::getRoughAmount (SlotID slot) const
  202. {
  203. int quantity = CCreature::getQuantityID(getStackCount(slot));
  204. if (quantity)
  205. return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getStackCount(slot))];
  206. return "";
  207. }
  208. int CCreatureSet::stacksCount() const
  209. {
  210. return stacks.size();
  211. }
  212. void CCreatureSet::setFormation(bool tight)
  213. {
  214. formation = tight;
  215. }
  216. void CCreatureSet::setStackCount(SlotID slot, TQuantity count)
  217. {
  218. assert(hasStackAtSlot(slot));
  219. assert(stacks[slot]->count + count > 0);
  220. if (VLC->modh->modules.STACK_EXP && count > stacks[slot]->count)
  221. stacks[slot]->experience *= (count / static_cast<double>(stacks[slot]->count));
  222. stacks[slot]->count = count;
  223. armyChanged();
  224. }
  225. void CCreatureSet::giveStackExp(TExpType exp)
  226. {
  227. for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); i++)
  228. i->second->giveStackExp(exp);
  229. }
  230. void CCreatureSet::setStackExp(SlotID slot, TExpType exp)
  231. {
  232. assert(hasStackAtSlot(slot));
  233. stacks[slot]->experience = exp;
  234. }
  235. void CCreatureSet::clear()
  236. {
  237. while(!stacks.empty())
  238. {
  239. eraseStack(stacks.begin()->first);
  240. }
  241. }
  242. const CStackInstance& CCreatureSet::getStack(SlotID slot) const
  243. {
  244. assert(hasStackAtSlot(slot));
  245. return *getStackPtr(slot);
  246. }
  247. const CStackInstance* CCreatureSet::getStackPtr(SlotID slot) const
  248. {
  249. if(hasStackAtSlot(slot))
  250. return stacks.find(slot)->second;
  251. else return NULL;
  252. }
  253. void CCreatureSet::eraseStack(SlotID slot)
  254. {
  255. assert(hasStackAtSlot(slot));
  256. CStackInstance *toErase = detachStack(slot);
  257. vstd::clear_pointer(toErase);
  258. }
  259. bool CCreatureSet::contains(const CStackInstance *stack) const
  260. {
  261. if(!stack)
  262. return false;
  263. for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); ++i)
  264. if(i->second == stack)
  265. return true;
  266. return false;
  267. }
  268. SlotID CCreatureSet::findStack(const CStackInstance *stack) const
  269. {
  270. auto h = dynamic_cast<const CGHeroInstance *>(this);
  271. if (h && h->commander == stack)
  272. return SlotID::COMMANDER_SLOT_PLACEHOLDER;
  273. if(!stack)
  274. return SlotID();
  275. for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); ++i)
  276. if(i->second == stack)
  277. return i->first;
  278. return SlotID();
  279. }
  280. CArmedInstance * CCreatureSet::castToArmyObj()
  281. {
  282. return dynamic_cast<CArmedInstance *>(this);
  283. }
  284. void CCreatureSet::putStack(SlotID slot, CStackInstance *stack)
  285. {
  286. assert(slot.getNum() < GameConstants::ARMY_SIZE);
  287. assert(!hasStackAtSlot(slot));
  288. stacks[slot] = stack;
  289. stack->setArmyObj(castToArmyObj());
  290. armyChanged();
  291. }
  292. void CCreatureSet::joinStack(SlotID slot, CStackInstance * stack)
  293. {
  294. const CCreature *c = getCreature(slot);
  295. assert(c == stack->type);
  296. assert(c);
  297. //TODO move stuff
  298. changeStackCount(slot, stack->count);
  299. vstd::clear_pointer(stack);
  300. }
  301. void CCreatureSet::changeStackCount(SlotID slot, TQuantity toAdd)
  302. {
  303. setStackCount(slot, getStackCount(slot) + toAdd);
  304. }
  305. CCreatureSet::CCreatureSet()
  306. {
  307. formation = false;
  308. }
  309. CCreatureSet::CCreatureSet(const CCreatureSet&)
  310. {
  311. assert(0);
  312. }
  313. CCreatureSet::~CCreatureSet()
  314. {
  315. clear();
  316. }
  317. void CCreatureSet::setToArmy(CSimpleArmy &src)
  318. {
  319. clear();
  320. while(src)
  321. {
  322. TSimpleSlots::iterator i = src.army.begin();
  323. assert(i->second.type);
  324. assert(i->second.count);
  325. putStack(i->first, new CStackInstance(i->second.type, i->second.count));
  326. src.army.erase(i);
  327. }
  328. }
  329. CStackInstance * CCreatureSet::detachStack(SlotID slot)
  330. {
  331. assert(hasStackAtSlot(slot));
  332. CStackInstance *ret = stacks[slot];
  333. //if(CArmedInstance *armedObj = castToArmyObj())
  334. {
  335. ret->setArmyObj(NULL); //detaches from current armyobj
  336. }
  337. assert(!ret->armyObj); //we failed detaching?
  338. stacks.erase(slot);
  339. armyChanged();
  340. return ret;
  341. }
  342. void CCreatureSet::setStackType(SlotID slot, const CCreature *type)
  343. {
  344. assert(hasStackAtSlot(slot));
  345. CStackInstance *s = stacks[slot];
  346. s->setType(type->idNumber);
  347. armyChanged();
  348. }
  349. bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks) const
  350. {
  351. if(!allowMergingStacks)
  352. {
  353. int freeSlots = stacksCount() - GameConstants::ARMY_SIZE;
  354. std::set<const CCreature*> cresToAdd;
  355. for(TSlots::const_iterator i = cs.stacks.begin(); i != cs.stacks.end(); i++)
  356. {
  357. SlotID dest = getSlotFor(i->second->type);
  358. if(!dest.validSlot() || hasStackAtSlot(dest))
  359. cresToAdd.insert(i->second->type);
  360. }
  361. return cresToAdd.size() <= freeSlots;
  362. }
  363. else
  364. {
  365. CCreatureSet cres;
  366. //get types of creatures that need their own slot
  367. for(TSlots::const_iterator i = cs.stacks.begin(); i != cs.stacks.end(); i++)
  368. cres.addToSlot(i->first, i->second->type->idNumber, 1, true);
  369. SlotID j;
  370. for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); i++)
  371. {
  372. if ((j = cres.getSlotFor(i->second->type)).validSlot())
  373. cres.addToSlot(j, i->second->type->idNumber, 1, true); //merge if possible
  374. else
  375. return false; //no place found
  376. }
  377. return true; //all stacks found their slots
  378. }
  379. }
  380. bool CCreatureSet::hasStackAtSlot(SlotID slot) const
  381. {
  382. return vstd::contains(stacks, slot);
  383. }
  384. CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs)
  385. {
  386. assert(0);
  387. return *this;
  388. }
  389. void CCreatureSet::armyChanged()
  390. {
  391. }
  392. CStackInstance::CStackInstance()
  393. : armyObj(_armyObj)
  394. {
  395. init();
  396. }
  397. CStackInstance::CStackInstance(CreatureID id, TQuantity Count)
  398. : armyObj(_armyObj)
  399. {
  400. init();
  401. setType(id);
  402. count = Count;
  403. }
  404. CStackInstance::CStackInstance(const CCreature *cre, TQuantity Count)
  405. : armyObj(_armyObj)
  406. {
  407. init();
  408. setType(cre);
  409. count = Count;
  410. }
  411. void CStackInstance::init()
  412. {
  413. experience = 0;
  414. count = 0;
  415. type = NULL;
  416. idRand = -1;
  417. _armyObj = NULL;
  418. setNodeType(STACK_INSTANCE);
  419. }
  420. int CStackInstance::getQuantityID() const
  421. {
  422. return CCreature::getQuantityID(count);
  423. }
  424. int CStackInstance::getExpRank() const
  425. {
  426. if (!VLC->modh->modules.STACK_EXP)
  427. return 0;
  428. int tier = type->level;
  429. if (vstd::iswithin(tier, 1, 7))
  430. {
  431. for (int i = VLC->creh->expRanks[tier].size()-2; i >-1; --i)//sic!
  432. { //exp values vary from 1st level to max exp at 11th level
  433. if (experience >= VLC->creh->expRanks[tier][i])
  434. return ++i; //faster, but confusing - 0 index mean 1st level of experience
  435. }
  436. return 0;
  437. }
  438. else //higher tier
  439. {
  440. for (int i = VLC->creh->expRanks[0].size()-2; i >-1; --i)
  441. {
  442. if (experience >= VLC->creh->expRanks[0][i])
  443. return ++i;
  444. }
  445. return 0;
  446. }
  447. }
  448. si32 CStackInstance::magicResistance() const
  449. {
  450. si32 val = valOfBonuses(Selector::type(Bonus::MAGIC_RESISTANCE));
  451. if (const CGHeroInstance * hero = dynamic_cast<const CGHeroInstance *>(_armyObj))
  452. {
  453. //resistance skill
  454. val += hero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::RESISTANCE);
  455. }
  456. vstd::amin (val, 100);
  457. return val;
  458. }
  459. void CStackInstance::giveStackExp(TExpType exp)
  460. {
  461. int level = type->level;
  462. if (!vstd::iswithin(level, 1, 7))
  463. level = 0;
  464. CCreatureHandler * creh = VLC->creh;
  465. ui32 maxExp = creh->expRanks[level].back();
  466. vstd::amin(exp, (TExpType)maxExp); //prevent exp overflow due to different types
  467. vstd::amin(exp, (maxExp * creh->maxExpPerBattle[level])/100);
  468. vstd::amin(experience += exp, maxExp); //can't get more exp than this limit
  469. }
  470. void CStackInstance::setType(CreatureID creID)
  471. {
  472. if(creID >= 0 && creID < VLC->creh->creatures.size())
  473. setType(VLC->creh->creatures[creID]);
  474. else
  475. setType((const CCreature*)NULL);
  476. }
  477. void CStackInstance::setType(const CCreature *c)
  478. {
  479. if(type)
  480. {
  481. detachFrom(const_cast<CCreature*>(type));
  482. if (type->isMyUpgrade(c) && VLC->modh->modules.STACK_EXP)
  483. experience *= VLC->creh->expAfterUpgrade / 100.0;
  484. }
  485. type = c;
  486. if(type)
  487. attachTo(const_cast<CCreature*>(type));
  488. }
  489. std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
  490. {
  491. std::map<Bonus::BonusType, std::pair<std::string, std::string> >::iterator it = VLC->creh->stackBonuses.find(bonus->type);
  492. if (it != VLC->creh->stackBonuses.end())
  493. {
  494. std::string text;
  495. if (description) //long ability description
  496. {
  497. text = it->second.second;
  498. switch (bonus->type)
  499. {
  500. //no additional modifiers needed
  501. case Bonus::FLYING:
  502. case Bonus::UNLIMITED_RETALIATIONS:
  503. case Bonus::SHOOTER:
  504. case Bonus::FREE_SHOOTING:
  505. case Bonus::NO_MELEE_PENALTY:
  506. case Bonus::NO_DISTANCE_PENALTY:
  507. case Bonus::NO_WALL_PENALTY:
  508. case Bonus::JOUSTING: //TODO: percent bonus?
  509. case Bonus::RETURN_AFTER_STRIKE:
  510. case Bonus::BLOCKS_RETALIATION:
  511. case Bonus::TWO_HEX_ATTACK_BREATH:
  512. case Bonus::THREE_HEADED_ATTACK:
  513. case Bonus::ATTACKS_ALL_ADJACENT:
  514. case Bonus::ADDITIONAL_ATTACK: //TODO: what with more than one attack? Axe of Ferocity for example
  515. case Bonus::FULL_HP_REGENERATION:
  516. case Bonus::MANA_DRAIN:
  517. case Bonus::LIFE_DRAIN:
  518. case Bonus::REBIRTH:
  519. case Bonus::SELF_MORALE:
  520. case Bonus::SELF_LUCK:
  521. case Bonus::FEAR:
  522. case Bonus::FEARLESS:
  523. case Bonus::CHARGE_IMMUNITY:
  524. case Bonus::HEALER:
  525. case Bonus::CATAPULT:
  526. case Bonus::DRAGON_NATURE:
  527. case Bonus::NON_LIVING:
  528. case Bonus::UNDEAD:
  529. case Bonus::FIRE_IMMUNITY:
  530. case Bonus::WATER_IMMUNITY:
  531. case Bonus::AIR_IMMUNITY:
  532. case Bonus::EARTH_IMMUNITY:
  533. case Bonus::RECEPTIVE:
  534. case Bonus::DIRECT_DAMAGE_IMMUNITY:
  535. break;
  536. //One numeric value. magic resistance handled separately
  537. case Bonus::SPELL_RESISTANCE_AURA:
  538. case Bonus::SPELL_DAMAGE_REDUCTION:
  539. case Bonus::LEVEL_SPELL_IMMUNITY:
  540. case Bonus::HP_REGENERATION:
  541. case Bonus::ADDITIONAL_RETALIATION:
  542. case Bonus::DEFENSIVE_STANCE:
  543. case Bonus::DOUBLE_DAMAGE_CHANCE:
  544. case Bonus::DARKNESS: //Darkness Dragons any1?
  545. boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
  546. break;
  547. //Complex descriptions
  548. //case Bonus::SECONDARY_SKILL_PREMY: //only if there's no simple MR
  549. // if (bonus->subtype == CGHeroInstance::RESISTANCE)
  550. // {
  551. // if (!hasBonusOfType(Bonus::MAGIC_RESISTANCE))
  552. // boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>( magicResistance() ));
  553. // }
  554. // break;
  555. //case Bonus::MAGIC_RESISTANCE:
  556. // boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>( magicResistance() ));
  557. // break;
  558. case Bonus::HATE:
  559. boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
  560. boost::algorithm::replace_first(text, "%s", VLC->creh->creatures[bonus->subtype]->namePl);
  561. break;
  562. case Bonus::SPELL_AFTER_ATTACK:
  563. case Bonus::SPELL_BEFORE_ATTACK:
  564. {
  565. boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
  566. boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
  567. break;
  568. }
  569. case Bonus::SPELL_LIKE_ATTACK:
  570. {
  571. boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
  572. break;
  573. }
  574. default:
  575. {}//TODO: allow custom bonus types... someday, somehow
  576. }
  577. }
  578. else //short name
  579. {
  580. text = it->second.first;
  581. switch (bonus->type)
  582. {
  583. case Bonus::MANA_CHANNELING:
  584. case Bonus::MAGIC_MIRROR:
  585. case Bonus::CHANGES_SPELL_COST_FOR_ALLY:
  586. case Bonus::CHANGES_SPELL_COST_FOR_ENEMY:
  587. case Bonus::ENEMY_DEFENCE_REDUCTION:
  588. case Bonus::REBIRTH:
  589. case Bonus::DEATH_STARE:
  590. case Bonus::LIFE_DRAIN:
  591. boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
  592. break;
  593. case Bonus::HATE:
  594. case Bonus::DAEMON_SUMMONING:
  595. boost::algorithm::replace_first(text, "%s", VLC->creh->creatures[bonus->subtype]->namePl);
  596. break;
  597. case Bonus::LEVEL_SPELL_IMMUNITY:
  598. boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bonus->val));
  599. break;
  600. case Bonus::SPELL_AFTER_ATTACK:
  601. case Bonus::SPELL_BEFORE_ATTACK:
  602. case Bonus::SPELL_IMMUNITY:
  603. case Bonus::SPELLCASTER:
  604. case Bonus::ENCHANTER:
  605. case Bonus::ENCHANTED:
  606. boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
  607. break;
  608. case Bonus::MAGIC_RESISTANCE:
  609. text = ""; //handled separately
  610. break;
  611. //case Bonus::SECONDARY_SKILL_PREMY:
  612. // if (bonus->subtype != CGHeroInstance::RESISTANCE || hasBonusOfType(Bonus::MAGIC_RESISTANCE)) //handle it there
  613. // text = "";
  614. // break;
  615. }
  616. }
  617. return text;
  618. }
  619. else
  620. return "";
  621. }
  622. std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
  623. {
  624. std::string fileName;
  625. bool fullPath = false;
  626. switch (bonus->type)
  627. {
  628. //"E_ALIVE.bmp"
  629. //"E_ART.bmp"
  630. case Bonus::ENCHANTED:
  631. fileName = "E_BLESS.bmp"; break;
  632. //"E_BLOCK.bmp"
  633. //"E_BLOCK1.bmp"
  634. //"E_BLOCK2.bmp"
  635. case Bonus::TWO_HEX_ATTACK_BREATH:
  636. fileName = "E_BREATH.bmp"; break;
  637. case Bonus::SPELL_AFTER_ATTACK:
  638. fileName = "E_CAST.bmp"; break;
  639. case Bonus::ENCHANTER:
  640. fileName = "E_CAST1.bmp"; break;
  641. case Bonus::RANDOM_SPELLCASTER:
  642. fileName = "RandomBoost.bmp"; break;
  643. case Bonus::SPELL_BEFORE_ATTACK:
  644. fileName ="E_CAST2.bmp"; break;
  645. case Bonus::SPELLCASTER:
  646. fileName = "E_CASTER.bmp"; break;
  647. case Bonus::JOUSTING:
  648. fileName = "E_CHAMP.bmp"; break;
  649. case Bonus::DOUBLE_DAMAGE_CHANCE:
  650. fileName = "E_DBLOW.bmp"; break;
  651. case Bonus::DEATH_STARE:
  652. fileName = "E_DEATH.bmp"; break;
  653. case Bonus::DEFENSIVE_STANCE:
  654. fileName = "E_DEFBON.bmp"; break;
  655. case Bonus::NO_DISTANCE_PENALTY:
  656. fileName = "E_DIST.bmp"; break;
  657. case Bonus::ADDITIONAL_ATTACK:
  658. fileName = "E_DOUBLE.bmp"; break;
  659. case Bonus::DRAGON_NATURE:
  660. fileName = "E_DRAGON.bmp"; break;
  661. case Bonus::MAGIC_RESISTANCE:
  662. fileName = "E_DWARF.bmp"; break;
  663. case Bonus::SECONDARY_SKILL_PREMY:
  664. if (bonus->subtype == SecondarySkill::RESISTANCE)
  665. {
  666. fileName = "E_DWARF.bmp";
  667. }
  668. break;
  669. case Bonus::FEAR:
  670. fileName = "E_FEAR.bmp"; break;
  671. case Bonus::FEARLESS:
  672. fileName = "E_FEARL.bmp"; break;
  673. case Bonus::FLYING:
  674. fileName = "E_FLY.bmp"; break;
  675. case Bonus::SPELL_DAMAGE_REDUCTION:
  676. fileName = "E_GOLEM.bmp"; break;
  677. case Bonus::RETURN_AFTER_STRIKE:
  678. fileName = "E_HARPY.bmp"; break;
  679. case Bonus::HATE:
  680. fileName = "E_HATE.bmp"; break;
  681. case Bonus::KING1:
  682. fileName = "E_KING1.bmp"; break;
  683. case Bonus::KING2:
  684. fileName = "E_KING2.bmp"; break;
  685. case Bonus::KING3:
  686. fileName = "E_KING3.bmp"; break;
  687. case Bonus::CHANGES_SPELL_COST_FOR_ALLY:
  688. fileName = "E_MANA.bmp"; break;
  689. case Bonus::CHANGES_SPELL_COST_FOR_ENEMY:
  690. fileName = "MagicDamper.bpm"; break;
  691. case Bonus::NO_MELEE_PENALTY:
  692. fileName = "E_MELEE.bmp"; break;
  693. case Bonus::MIND_IMMUNITY:
  694. fileName = "E_MIND.bmp"; break;
  695. case Bonus::SELF_MORALE:
  696. fileName = "E_MINOT.bmp"; break;
  697. case Bonus::NO_MORALE:
  698. fileName = "E_MORAL.bmp"; break;
  699. case Bonus::RECEPTIVE:
  700. fileName = "E_NOFRIM.bmp"; break;
  701. case Bonus::NO_WALL_PENALTY:
  702. fileName = "E_OBST.bmp"; break;
  703. case Bonus::ENEMY_DEFENCE_REDUCTION:
  704. fileName = "E_RDEF.bmp"; break;
  705. case Bonus::REBIRTH:
  706. fileName = "E_REBIRTH.bmp"; break;
  707. case Bonus::BLOCKS_RETALIATION:
  708. fileName = "E_RETAIL.bmp"; break;
  709. case Bonus::UNLIMITED_RETALIATIONS:
  710. case Bonus::ADDITIONAL_RETALIATION:
  711. fileName = "E_RETAIL1.bmp"; break;
  712. case Bonus::ATTACKS_ALL_ADJACENT:
  713. fileName = "E_ROUND.bmp"; break;
  714. //"E_SGNUM.bmp"
  715. //"E_SGTYPE.bmp"
  716. case Bonus::SHOOTER:
  717. fileName = "E_SHOOT.bmp"; break;
  718. case Bonus::FREE_SHOOTING: //shooter is not blocked by enemy
  719. fileName = "E_SHOOTA.bmp"; break;
  720. //"E_SHOOTN.bmp"
  721. case Bonus::SPELL_IMMUNITY:
  722. {
  723. fullPath = true;
  724. const CSpell * sp = SpellID(bonus->subtype).toSpell();
  725. fileName = sp->getIconImmune();
  726. break;
  727. }
  728. //"E_SPAWILL.bmp"
  729. case Bonus::DIRECT_DAMAGE_IMMUNITY:
  730. fileName = "E_SPDIR.bmp"; break;
  731. //"E_SPDISB.bmp"
  732. //"E_SPDISP.bmp"
  733. //"E_SPEATH.bmp"
  734. //"E_SPEATH1.bmp"
  735. case Bonus::FIRE_IMMUNITY:
  736. switch (bonus->subtype)
  737. {
  738. case 0:
  739. fileName = "E_SPFIRE.bmp"; break; //all
  740. case 1:
  741. fileName = "E_SPFIRE1.bmp"; break; //not positive
  742. case 2:
  743. fileName = "E_FIRE.bmp"; break; //direct damage
  744. }
  745. break;
  746. case Bonus::WATER_IMMUNITY:
  747. switch (bonus->subtype)
  748. {
  749. case 0:
  750. fileName = "E_SPWATER.bmp"; break; //all
  751. case 1:
  752. fileName = "E_SPWATER1.bmp"; break; //not positive
  753. case 2:
  754. fileName = "E_SPCOLD.bmp"; break; //direct damage
  755. }
  756. break;
  757. case Bonus::AIR_IMMUNITY:
  758. switch (bonus->subtype)
  759. {
  760. case 0:
  761. fileName = "E_SPAIR.bmp"; break; //all
  762. case 1:
  763. fileName = "E_SPAIR1.bmp"; break; //not positive
  764. case 2:
  765. fileName = "E_LIGHT.bmp"; break;//direct damage
  766. }
  767. break;
  768. case Bonus::EARTH_IMMUNITY:
  769. switch (bonus->subtype)
  770. {
  771. case 0:
  772. fileName = "E_SPEATH.bmp"; break; //all
  773. case 1:
  774. case 2: //no specific icon for direct damage immunity
  775. fileName = "E_SPEATH1.bmp"; break; //not positive
  776. }
  777. break;
  778. case Bonus::LEVEL_SPELL_IMMUNITY:
  779. {
  780. if (vstd::iswithin(bonus->val, 1 , 5))
  781. {
  782. fileName = "E_SPLVL" + boost::lexical_cast<std::string>(bonus->val) + ".bmp";
  783. }
  784. break;
  785. }
  786. //"E_SUMMON.bmp"
  787. //"E_SUMMON1.bmp"
  788. //"E_SUMMON2.bmp"
  789. case Bonus::FULL_HP_REGENERATION:
  790. case Bonus::HP_REGENERATION:
  791. fileName = "E_TROLL.bmp"; break;
  792. case Bonus::UNDEAD:
  793. fileName = "E_UNDEAD.bmp"; break;
  794. case Bonus::SPELL_RESISTANCE_AURA:
  795. fileName = "E_UNIC.bmp"; break;
  796. case Bonus::THREE_HEADED_ATTACK:
  797. fileName = "ThreeHeaded.bmp"; break;
  798. case Bonus::DAEMON_SUMMONING:
  799. fileName = "RiseDemons.bmp"; break;
  800. case Bonus::CHARGE_IMMUNITY:
  801. fileName = "ChargeImmune.bmp"; break;
  802. case Bonus::HEALER:
  803. fileName = "Healer.bmp"; break;
  804. case Bonus::CATAPULT:
  805. fileName = "Catapult.bmp"; break;
  806. case Bonus::MANA_CHANNELING:
  807. fileName = "ManaChannel.bmp"; break;
  808. case Bonus::MANA_DRAIN:
  809. fileName = "ManaDrain.bmp"; break;
  810. case Bonus::LIFE_DRAIN:
  811. fileName = "DrainLife.bmp"; break;
  812. }
  813. if(!fileName.empty() && !fullPath)
  814. fileName = "zvs/Lib1.res/" + fileName;
  815. return fileName;
  816. }
  817. void CStackInstance::setArmyObj(const CArmedInstance *ArmyObj)
  818. {
  819. if(_armyObj)
  820. detachFrom(const_cast<CArmedInstance*>(_armyObj));
  821. _armyObj = ArmyObj;
  822. if(ArmyObj)
  823. {
  824. attachTo(const_cast<CArmedInstance*>(_armyObj));
  825. }
  826. }
  827. std::string CStackInstance::getQuantityTXT(bool capitalized /*= true*/) const
  828. {
  829. int quantity = getQuantityID();
  830. if (quantity)
  831. return VLC->generaltexth->arraytxt[174 + quantity*3 - 1 - capitalized];
  832. else
  833. return "";
  834. }
  835. bool CStackInstance::valid(bool allowUnrandomized) const
  836. {
  837. bool isRand = (idRand != -1);
  838. if(!isRand)
  839. {
  840. return (type && type == VLC->creh->creatures[type->idNumber]);
  841. }
  842. else
  843. return allowUnrandomized;
  844. }
  845. CStackInstance::~CStackInstance()
  846. {
  847. }
  848. std::string CStackInstance::nodeName() const
  849. {
  850. std::ostringstream oss;
  851. oss << "Stack of " << count << " of ";
  852. if(type)
  853. oss << type->namePl;
  854. else if(idRand)
  855. oss << "[no type, idRand=" << idRand << "]";
  856. else
  857. oss << "[UNDEFINED TYPE]";
  858. return oss.str();
  859. }
  860. void CStackInstance::deserializationFix()
  861. {
  862. const CCreature *backup = type;
  863. type = NULL;
  864. setType(backup);
  865. const CArmedInstance *armyBackup = _armyObj;
  866. _armyObj = NULL;
  867. setArmyObj(armyBackup);
  868. artDeserializationFix(this);
  869. }
  870. CreatureID CStackInstance::getCreatureID() const
  871. {
  872. if(type)
  873. return type->idNumber;
  874. else
  875. return CreatureID::NONE;
  876. }
  877. std::string CStackInstance::getName() const
  878. {
  879. return (count > 1) ? type->namePl : type->nameSing;
  880. }
  881. ui64 CStackInstance::getPower() const
  882. {
  883. assert(type);
  884. return type->AIValue * count;
  885. }
  886. ArtBearer::ArtBearer CStackInstance::bearerType() const
  887. {
  888. return ArtBearer::CREATURE;
  889. }
  890. CCommanderInstance::CCommanderInstance()
  891. {
  892. init();
  893. name = "Unnamed";
  894. }
  895. CCommanderInstance::CCommanderInstance (CreatureID id)
  896. {
  897. init();
  898. setType(id);
  899. name = "Commando"; //TODO - parse them
  900. }
  901. void CCommanderInstance::init()
  902. {
  903. alive = true;
  904. experience = 0;
  905. level = 1;
  906. count = 1;
  907. type = NULL;
  908. idRand = -1;
  909. _armyObj = NULL;
  910. setNodeType (CBonusSystemNode::COMMANDER);
  911. secondarySkills.resize (ECommander::SPELL_POWER + 1);
  912. }
  913. CCommanderInstance::~CCommanderInstance()
  914. {
  915. }
  916. void CCommanderInstance::setAlive (bool Alive)
  917. {
  918. //TODO: helm of immortality
  919. alive = Alive;
  920. if (!alive)
  921. {
  922. getBonusList().remove_if (Bonus::UntilCommanderKilled);
  923. }
  924. }
  925. void CCommanderInstance::giveStackExp (TExpType exp)
  926. {
  927. if (alive)
  928. experience += exp;
  929. }
  930. int CCommanderInstance::getExpRank() const
  931. {
  932. return VLC->heroh->level (experience);
  933. }
  934. void CCommanderInstance::levelUp ()
  935. {
  936. level++;
  937. BOOST_FOREACH (auto bonus, VLC->creh->commanderLevelPremy)
  938. { //grant all regular level-up bonuses
  939. accumulateBonus (*bonus);
  940. }
  941. }
  942. ArtBearer::ArtBearer CCommanderInstance::bearerType() const
  943. {
  944. return ArtBearer::COMMANDER;
  945. }
  946. CStackBasicDescriptor::CStackBasicDescriptor()
  947. {
  948. type = NULL;
  949. count = -1;
  950. }
  951. CStackBasicDescriptor::CStackBasicDescriptor(CreatureID id, TQuantity Count)
  952. : type (VLC->creh->creatures[id]), count(Count)
  953. {
  954. }
  955. CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count)
  956. : type(c), count(Count)
  957. {
  958. }
  959. DLL_LINKAGE std::ostream & operator<<(std::ostream & str, const CStackInstance & sth)
  960. {
  961. if(!sth.valid(true))
  962. str << "an invalid stack!";
  963. str << "stack with " << sth.count << " of ";
  964. if(sth.type)
  965. str << sth.type->namePl;
  966. else
  967. str << sth.idRand;
  968. return str;
  969. }
  970. void CSimpleArmy::clear()
  971. {
  972. army.clear();
  973. }
  974. CSimpleArmy::operator bool() const
  975. {
  976. return army.size();
  977. }
  978. bool CSimpleArmy::setCreature(SlotID slot, CreatureID cre, TQuantity count)
  979. {
  980. assert(!vstd::contains(army, slot));
  981. army[slot] = CStackBasicDescriptor(cre, count);
  982. return true;
  983. }