CCreatureSet.cpp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  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. case Bonus::FIRE_SHIELD:
  592. boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
  593. break;
  594. case Bonus::HATE:
  595. case Bonus::DAEMON_SUMMONING:
  596. boost::algorithm::replace_first(text, "%s", VLC->creh->creatures[bonus->subtype]->namePl);
  597. break;
  598. case Bonus::LEVEL_SPELL_IMMUNITY:
  599. boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bonus->val));
  600. break;
  601. case Bonus::SPELL_AFTER_ATTACK:
  602. case Bonus::SPELL_BEFORE_ATTACK:
  603. case Bonus::SPELL_IMMUNITY:
  604. case Bonus::SPELLCASTER:
  605. case Bonus::ENCHANTER:
  606. case Bonus::ENCHANTED:
  607. boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
  608. break;
  609. case Bonus::MAGIC_RESISTANCE:
  610. text = ""; //handled separately
  611. break;
  612. //case Bonus::SECONDARY_SKILL_PREMY:
  613. // if (bonus->subtype != CGHeroInstance::RESISTANCE || hasBonusOfType(Bonus::MAGIC_RESISTANCE)) //handle it there
  614. // text = "";
  615. // break;
  616. }
  617. }
  618. return text;
  619. }
  620. else
  621. return "";
  622. }
  623. std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
  624. {
  625. std::string fileName;
  626. bool fullPath = false;
  627. switch (bonus->type)
  628. {
  629. //"E_ALIVE.bmp"
  630. //"E_ART.bmp"
  631. case Bonus::ENCHANTED:
  632. fileName = "E_BLESS.bmp"; break;
  633. //"E_BLOCK.bmp"
  634. //"E_BLOCK1.bmp"
  635. //"E_BLOCK2.bmp"
  636. case Bonus::TWO_HEX_ATTACK_BREATH:
  637. fileName = "E_BREATH.bmp"; break;
  638. case Bonus::SPELL_AFTER_ATTACK:
  639. fileName = "E_CAST.bmp"; break;
  640. case Bonus::ENCHANTER:
  641. fileName = "E_CAST1.bmp"; break;
  642. case Bonus::RANDOM_SPELLCASTER:
  643. fileName = "RandomBoost.bmp"; break;
  644. case Bonus::SPELL_BEFORE_ATTACK:
  645. fileName ="E_CAST2.bmp"; break;
  646. case Bonus::SPELLCASTER:
  647. fileName = "E_CASTER.bmp"; break;
  648. case Bonus::JOUSTING:
  649. fileName = "E_CHAMP.bmp"; break;
  650. case Bonus::DOUBLE_DAMAGE_CHANCE:
  651. fileName = "E_DBLOW.bmp"; break;
  652. case Bonus::DEATH_STARE:
  653. fileName = "E_DEATH.bmp"; break;
  654. case Bonus::DEFENSIVE_STANCE:
  655. fileName = "E_DEFBON.bmp"; break;
  656. case Bonus::NO_DISTANCE_PENALTY:
  657. fileName = "E_DIST.bmp"; break;
  658. case Bonus::ADDITIONAL_ATTACK:
  659. fileName = "E_DOUBLE.bmp"; break;
  660. case Bonus::DRAGON_NATURE:
  661. fileName = "E_DRAGON.bmp"; break;
  662. case Bonus::MAGIC_RESISTANCE:
  663. fileName = "E_DWARF.bmp"; break;
  664. case Bonus::SECONDARY_SKILL_PREMY:
  665. if (bonus->subtype == SecondarySkill::RESISTANCE)
  666. {
  667. fileName = "E_DWARF.bmp";
  668. }
  669. break;
  670. case Bonus::FEAR:
  671. fileName = "E_FEAR.bmp"; break;
  672. case Bonus::FEARLESS:
  673. fileName = "E_FEARL.bmp"; break;
  674. case Bonus::FLYING:
  675. fileName = "E_FLY.bmp"; break;
  676. case Bonus::SPELL_DAMAGE_REDUCTION:
  677. fileName = "E_GOLEM.bmp"; break;
  678. case Bonus::RETURN_AFTER_STRIKE:
  679. fileName = "E_HARPY.bmp"; break;
  680. case Bonus::HATE:
  681. fileName = "E_HATE.bmp"; break;
  682. case Bonus::KING1:
  683. fileName = "E_KING1.bmp"; break;
  684. case Bonus::KING2:
  685. fileName = "E_KING2.bmp"; break;
  686. case Bonus::KING3:
  687. fileName = "E_KING3.bmp"; break;
  688. case Bonus::CHANGES_SPELL_COST_FOR_ALLY:
  689. fileName = "E_MANA.bmp"; break;
  690. case Bonus::CHANGES_SPELL_COST_FOR_ENEMY:
  691. fileName = "MagicDamper.bpm"; break;
  692. case Bonus::NO_MELEE_PENALTY:
  693. fileName = "E_MELEE.bmp"; break;
  694. case Bonus::MIND_IMMUNITY:
  695. fileName = "E_MIND.bmp"; break;
  696. case Bonus::SELF_MORALE:
  697. fileName = "E_MINOT.bmp"; break;
  698. case Bonus::NO_MORALE:
  699. fileName = "E_MORAL.bmp"; break;
  700. case Bonus::RECEPTIVE:
  701. fileName = "E_NOFRIM.bmp"; break;
  702. case Bonus::NO_WALL_PENALTY:
  703. fileName = "E_OBST.bmp"; break;
  704. case Bonus::ENEMY_DEFENCE_REDUCTION:
  705. fileName = "E_RDEF.bmp"; break;
  706. case Bonus::REBIRTH:
  707. fileName = "E_REBIRTH.bmp"; break;
  708. case Bonus::BLOCKS_RETALIATION:
  709. fileName = "E_RETAIL.bmp"; break;
  710. case Bonus::UNLIMITED_RETALIATIONS:
  711. case Bonus::ADDITIONAL_RETALIATION:
  712. fileName = "E_RETAIL1.bmp"; break;
  713. case Bonus::ATTACKS_ALL_ADJACENT:
  714. fileName = "E_ROUND.bmp"; break;
  715. //"E_SGNUM.bmp"
  716. //"E_SGTYPE.bmp"
  717. case Bonus::SHOOTER:
  718. fileName = "E_SHOOT.bmp"; break;
  719. case Bonus::FREE_SHOOTING: //shooter is not blocked by enemy
  720. fileName = "E_SHOOTA.bmp"; break;
  721. //"E_SHOOTN.bmp"
  722. case Bonus::SPELL_IMMUNITY:
  723. {
  724. fullPath = true;
  725. const CSpell * sp = SpellID(bonus->subtype).toSpell();
  726. fileName = sp->getIconImmune();
  727. break;
  728. }
  729. //"E_SPAWILL.bmp"
  730. case Bonus::DIRECT_DAMAGE_IMMUNITY:
  731. fileName = "E_SPDIR.bmp"; break;
  732. //"E_SPDISB.bmp"
  733. //"E_SPDISP.bmp"
  734. //"E_SPEATH.bmp"
  735. //"E_SPEATH1.bmp"
  736. case Bonus::FIRE_IMMUNITY:
  737. switch (bonus->subtype)
  738. {
  739. case 0:
  740. fileName = "E_SPFIRE.bmp"; break; //all
  741. case 1:
  742. fileName = "E_SPFIRE1.bmp"; break; //not positive
  743. case 2:
  744. fileName = "E_FIRE.bmp"; break; //direct damage
  745. }
  746. break;
  747. case Bonus::WATER_IMMUNITY:
  748. switch (bonus->subtype)
  749. {
  750. case 0:
  751. fileName = "E_SPWATER.bmp"; break; //all
  752. case 1:
  753. fileName = "E_SPWATER1.bmp"; break; //not positive
  754. case 2:
  755. fileName = "E_SPCOLD.bmp"; break; //direct damage
  756. }
  757. break;
  758. case Bonus::AIR_IMMUNITY:
  759. switch (bonus->subtype)
  760. {
  761. case 0:
  762. fileName = "E_SPAIR.bmp"; break; //all
  763. case 1:
  764. fileName = "E_SPAIR1.bmp"; break; //not positive
  765. case 2:
  766. fileName = "E_LIGHT.bmp"; break;//direct damage
  767. }
  768. break;
  769. case Bonus::EARTH_IMMUNITY:
  770. switch (bonus->subtype)
  771. {
  772. case 0:
  773. fileName = "E_SPEATH.bmp"; break; //all
  774. case 1:
  775. case 2: //no specific icon for direct damage immunity
  776. fileName = "E_SPEATH1.bmp"; break; //not positive
  777. }
  778. break;
  779. case Bonus::LEVEL_SPELL_IMMUNITY:
  780. {
  781. if (vstd::iswithin(bonus->val, 1 , 5))
  782. {
  783. fileName = "E_SPLVL" + boost::lexical_cast<std::string>(bonus->val) + ".bmp";
  784. }
  785. break;
  786. }
  787. //"E_SUMMON.bmp"
  788. //"E_SUMMON1.bmp"
  789. //"E_SUMMON2.bmp"
  790. case Bonus::FULL_HP_REGENERATION:
  791. case Bonus::HP_REGENERATION:
  792. fileName = "E_TROLL.bmp"; break;
  793. case Bonus::UNDEAD:
  794. fileName = "E_UNDEAD.bmp"; break;
  795. case Bonus::SPELL_RESISTANCE_AURA:
  796. fileName = "E_UNIC.bmp"; break;
  797. case Bonus::THREE_HEADED_ATTACK:
  798. fileName = "ThreeHeaded.bmp"; break;
  799. case Bonus::DAEMON_SUMMONING:
  800. fileName = "RiseDemons.bmp"; break;
  801. case Bonus::CHARGE_IMMUNITY:
  802. fileName = "ChargeImmune.bmp"; break;
  803. case Bonus::HEALER:
  804. fileName = "Healer.bmp"; break;
  805. case Bonus::CATAPULT:
  806. fileName = "Catapult.bmp"; break;
  807. case Bonus::MANA_CHANNELING:
  808. fileName = "ManaChannel.bmp"; break;
  809. case Bonus::MANA_DRAIN:
  810. fileName = "ManaDrain.bmp"; break;
  811. case Bonus::LIFE_DRAIN:
  812. fileName = "DrainLife.bmp"; break;
  813. case Bonus::FIRE_SHIELD:
  814. fileName = "FireShield.bmp"; break;
  815. case Bonus::MAGIC_MIRROR:
  816. fileName = "MagicMirror.bmp"; break;
  817. case Bonus::NON_LIVING:
  818. fileName = "NonLiving.bmp"; break;
  819. case Bonus::SPELL_LIKE_ATTACK:
  820. fileName = "SpellLikeAttack.bmp"; break;
  821. }
  822. if(!fileName.empty() && !fullPath)
  823. fileName = "zvs/Lib1.res/" + fileName;
  824. return fileName;
  825. }
  826. void CStackInstance::setArmyObj(const CArmedInstance *ArmyObj)
  827. {
  828. if(_armyObj)
  829. detachFrom(const_cast<CArmedInstance*>(_armyObj));
  830. _armyObj = ArmyObj;
  831. if(ArmyObj)
  832. {
  833. attachTo(const_cast<CArmedInstance*>(_armyObj));
  834. }
  835. }
  836. std::string CStackInstance::getQuantityTXT(bool capitalized /*= true*/) const
  837. {
  838. int quantity = getQuantityID();
  839. if (quantity)
  840. return VLC->generaltexth->arraytxt[174 + quantity*3 - 1 - capitalized];
  841. else
  842. return "";
  843. }
  844. bool CStackInstance::valid(bool allowUnrandomized) const
  845. {
  846. bool isRand = (idRand != -1);
  847. if(!isRand)
  848. {
  849. return (type && type == VLC->creh->creatures[type->idNumber]);
  850. }
  851. else
  852. return allowUnrandomized;
  853. }
  854. CStackInstance::~CStackInstance()
  855. {
  856. }
  857. std::string CStackInstance::nodeName() const
  858. {
  859. std::ostringstream oss;
  860. oss << "Stack of " << count << " of ";
  861. if(type)
  862. oss << type->namePl;
  863. else if(idRand)
  864. oss << "[no type, idRand=" << idRand << "]";
  865. else
  866. oss << "[UNDEFINED TYPE]";
  867. return oss.str();
  868. }
  869. void CStackInstance::deserializationFix()
  870. {
  871. const CCreature *backup = type;
  872. type = NULL;
  873. setType(backup);
  874. const CArmedInstance *armyBackup = _armyObj;
  875. _armyObj = NULL;
  876. setArmyObj(armyBackup);
  877. artDeserializationFix(this);
  878. }
  879. CreatureID CStackInstance::getCreatureID() const
  880. {
  881. if(type)
  882. return type->idNumber;
  883. else
  884. return CreatureID::NONE;
  885. }
  886. std::string CStackInstance::getName() const
  887. {
  888. return (count > 1) ? type->namePl : type->nameSing;
  889. }
  890. ui64 CStackInstance::getPower() const
  891. {
  892. assert(type);
  893. return type->AIValue * count;
  894. }
  895. ArtBearer::ArtBearer CStackInstance::bearerType() const
  896. {
  897. return ArtBearer::CREATURE;
  898. }
  899. CCommanderInstance::CCommanderInstance()
  900. {
  901. init();
  902. name = "Unnamed";
  903. }
  904. CCommanderInstance::CCommanderInstance (CreatureID id)
  905. {
  906. init();
  907. setType(id);
  908. name = "Commando"; //TODO - parse them
  909. }
  910. void CCommanderInstance::init()
  911. {
  912. alive = true;
  913. experience = 0;
  914. level = 1;
  915. count = 1;
  916. type = NULL;
  917. idRand = -1;
  918. _armyObj = NULL;
  919. setNodeType (CBonusSystemNode::COMMANDER);
  920. secondarySkills.resize (ECommander::SPELL_POWER + 1);
  921. }
  922. CCommanderInstance::~CCommanderInstance()
  923. {
  924. }
  925. void CCommanderInstance::setAlive (bool Alive)
  926. {
  927. //TODO: helm of immortality
  928. alive = Alive;
  929. if (!alive)
  930. {
  931. getBonusList().remove_if (Bonus::UntilCommanderKilled);
  932. }
  933. }
  934. void CCommanderInstance::giveStackExp (TExpType exp)
  935. {
  936. if (alive)
  937. experience += exp;
  938. }
  939. int CCommanderInstance::getExpRank() const
  940. {
  941. return VLC->heroh->level (experience);
  942. }
  943. void CCommanderInstance::levelUp ()
  944. {
  945. level++;
  946. BOOST_FOREACH (auto bonus, VLC->creh->commanderLevelPremy)
  947. { //grant all regular level-up bonuses
  948. accumulateBonus (*bonus);
  949. }
  950. }
  951. ArtBearer::ArtBearer CCommanderInstance::bearerType() const
  952. {
  953. return ArtBearer::COMMANDER;
  954. }
  955. CStackBasicDescriptor::CStackBasicDescriptor()
  956. {
  957. type = NULL;
  958. count = -1;
  959. }
  960. CStackBasicDescriptor::CStackBasicDescriptor(CreatureID id, TQuantity Count)
  961. : type (VLC->creh->creatures[id]), count(Count)
  962. {
  963. }
  964. CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count)
  965. : type(c), count(Count)
  966. {
  967. }
  968. DLL_LINKAGE std::ostream & operator<<(std::ostream & str, const CStackInstance & sth)
  969. {
  970. if(!sth.valid(true))
  971. str << "an invalid stack!";
  972. str << "stack with " << sth.count << " of ";
  973. if(sth.type)
  974. str << sth.type->namePl;
  975. else
  976. str << sth.idRand;
  977. return str;
  978. }
  979. void CSimpleArmy::clear()
  980. {
  981. army.clear();
  982. }
  983. CSimpleArmy::operator bool() const
  984. {
  985. return army.size();
  986. }
  987. bool CSimpleArmy::setCreature(SlotID slot, CreatureID cre, TQuantity count)
  988. {
  989. assert(!vstd::contains(army, slot));
  990. army[slot] = CStackBasicDescriptor(cre, count);
  991. return true;
  992. }