CBank.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /*
  2. * CBank.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CBank.h"
  12. #include "../NetPacks.h"
  13. #include "../CGeneralTextHandler.h"
  14. #include "../CSoundBase.h"
  15. using namespace boost::assign;
  16. ///helpers
  17. static std::string & visitedTxt(const bool visited)
  18. {
  19. int id = visited ? 352 : 353;
  20. return VLC->generaltexth->allTexts[id];
  21. }
  22. void CBank::initObj()
  23. {
  24. index = VLC->objh->bankObjToIndex(this);
  25. bc = nullptr;
  26. daycounter = 0;
  27. multiplier = 1;
  28. }
  29. const std::string & CBank::getHoverText() const
  30. {
  31. bool visited = (bc == nullptr);
  32. hoverName = VLC->objh->creBanksNames[index] + " " + visitedTxt(visited);
  33. return hoverName;
  34. }
  35. void CBank::reset(ui16 var1) //prevents desync
  36. {
  37. ui8 chance = 0;
  38. for (auto & elem : VLC->objh->banksInfo[index])
  39. {
  40. if (var1 < (chance += elem->chance))
  41. {
  42. bc = elem;
  43. break;
  44. }
  45. }
  46. artifacts.clear();
  47. }
  48. void CBank::initialize() const
  49. {
  50. cb->setObjProperty(id, ObjProperty::BANK_RESET, cb->gameState()->getRandomGenerator().nextInt()); //synchronous reset
  51. for (ui8 i = 0; i <= 3; i++)
  52. {
  53. for (ui8 n = 0; n < bc->artifacts[i]; n++)
  54. {
  55. CArtifact::EartClass artClass;
  56. switch(i)
  57. {
  58. case 0: artClass = CArtifact::ART_TREASURE; break;
  59. case 1: artClass = CArtifact::ART_MINOR; break;
  60. case 2: artClass = CArtifact::ART_MAJOR; break;
  61. case 3: artClass = CArtifact::ART_RELIC; break;
  62. default: assert(0); continue;
  63. }
  64. int artID = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), artClass);
  65. cb->setObjProperty(id, ObjProperty::BANK_ADD_ARTIFACT, artID);
  66. }
  67. }
  68. cb->setObjProperty(id, ObjProperty::BANK_INIT_ARMY, cb->gameState()->getRandomGenerator().nextInt()); //get army
  69. }
  70. void CBank::setPropertyDer (ui8 what, ui32 val)
  71. /// random values are passed as arguments and processed identically on all clients
  72. {
  73. switch (what)
  74. {
  75. case ObjProperty::BANK_DAYCOUNTER: //daycounter
  76. if (val == 0)
  77. daycounter = 1; //yes, 1
  78. else
  79. daycounter++;
  80. break;
  81. case ObjProperty::BANK_MULTIPLIER: //multiplier, in percent
  82. multiplier = val / 100.0;
  83. break;
  84. case 13: //bank preset
  85. bc = VLC->objh->banksInfo[index][val];
  86. break;
  87. case ObjProperty::BANK_RESET:
  88. reset (val%100);
  89. break;
  90. case ObjProperty::BANK_CLEAR_CONFIG:
  91. bc = nullptr;
  92. break;
  93. case ObjProperty::BANK_CLEAR_ARTIFACTS: //remove rewards from Derelict Ship
  94. artifacts.clear();
  95. break;
  96. case ObjProperty::BANK_INIT_ARMY: //set ArmedInstance army
  97. {
  98. int upgraded = 0;
  99. if (val%100 < bc->upgradeChance) //once again anti-desync
  100. upgraded = 1;
  101. switch (bc->guards.size())
  102. {
  103. case 1:
  104. for (int i = 0; i < 4; ++i)
  105. setCreature (SlotID(i), bc->guards[0].first, bc->guards[0].second / 5 );
  106. setCreature (SlotID(4), CreatureID(bc->guards[0].first + upgraded), bc->guards[0].second / 5 );
  107. break;
  108. case 4:
  109. {
  110. if (bc->guards.back().second) //all stacks are present
  111. {
  112. for (auto & elem : bc->guards)
  113. {
  114. setCreature (SlotID(stacksCount()), elem.first, elem.second);
  115. }
  116. }
  117. else if (bc->guards[2].second)//Wraiths are present, split two stacks in Crypt
  118. {
  119. setCreature (SlotID(0), bc->guards[0].first, bc->guards[0].second / 2 );
  120. setCreature (SlotID(1), bc->guards[1].first, bc->guards[1].second / 2);
  121. setCreature (SlotID(2), CreatureID(bc->guards[2].first + upgraded), bc->guards[2].second);
  122. setCreature (SlotID(3), bc->guards[1].first, bc->guards[1].second / 2 );
  123. setCreature (SlotID(4), bc->guards[0].first, bc->guards[0].second - (bc->guards[0].second / 2) );
  124. }
  125. else //split both stacks
  126. {
  127. for (int i = 0; i < 3; ++i) //skellies
  128. setCreature (SlotID(2*i), bc->guards[0].first, bc->guards[0].second / 3);
  129. for (int i = 0; i < 2; ++i) //zombies
  130. setCreature (SlotID(2*i+1), bc->guards[1].first, bc->guards[1].second / 2);
  131. }
  132. }
  133. break;
  134. default:
  135. logGlobal->warnStream() << "Error: Unexpected army data: " << bc->guards.size() <<" items found";
  136. return;
  137. }
  138. }
  139. break;
  140. case ObjProperty::BANK_ADD_ARTIFACT: //add Artifact
  141. {
  142. artifacts.push_back (val);
  143. break;
  144. }
  145. }
  146. }
  147. void CBank::newTurn() const
  148. {
  149. if (bc == nullptr)
  150. {
  151. if (cb->getDate() == 1)
  152. initialize(); //initialize on first day
  153. else if (daycounter >= 28 && (subID < 13 || subID > 16)) //no reset for Emissaries
  154. {
  155. initialize();
  156. cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 0); //daycounter 0
  157. if (ID == Obj::DERELICT_SHIP && cb->getDate() > 1)
  158. {
  159. cb->setObjProperty (id, ObjProperty::BANK_MULTIPLIER, 0);//ugly hack to make derelict ships usable only once
  160. cb->setObjProperty (id, ObjProperty::BANK_CLEAR_ARTIFACTS, 0);
  161. }
  162. }
  163. else
  164. cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
  165. }
  166. }
  167. bool CBank::wasVisited (PlayerColor player) const
  168. {
  169. return !bc;
  170. }
  171. void CBank::onHeroVisit (const CGHeroInstance * h) const
  172. {
  173. if (bc)
  174. {
  175. int banktext = 0;
  176. switch (ID)
  177. {
  178. case Obj::CREATURE_BANK:
  179. banktext = 32;
  180. break;
  181. case Obj::DERELICT_SHIP:
  182. banktext = 41;
  183. break;
  184. case Obj::DRAGON_UTOPIA:
  185. banktext = 47;
  186. break;
  187. case Obj::CRYPT:
  188. banktext = 119;
  189. break;
  190. case Obj::SHIPWRECK:
  191. banktext = 122;
  192. break;
  193. }
  194. BlockingDialog bd (true, false);
  195. bd.player = h->getOwner();
  196. bd.soundID = soundBase::ROGUE;
  197. bd.text.addTxt(MetaString::ADVOB_TXT,banktext);
  198. if (ID == Obj::CREATURE_BANK)
  199. bd.text.addReplacement(VLC->objh->creBanksNames[index]);
  200. cb->showBlockingDialog (&bd);
  201. }
  202. else
  203. {
  204. InfoWindow iw;
  205. iw.soundID = soundBase::GRAVEYARD;
  206. iw.player = h->getOwner();
  207. if (ID == Obj::CRYPT) //morale penalty for empty Crypt
  208. {
  209. GiveBonus gbonus;
  210. gbonus.id = h->id.getNum();
  211. gbonus.bonus.duration = Bonus::ONE_BATTLE;
  212. gbonus.bonus.source = Bonus::OBJECT;
  213. gbonus.bonus.sid = ID;
  214. gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[98];
  215. gbonus.bonus.type = Bonus::MORALE;
  216. gbonus.bonus.val = -1;
  217. cb->giveHeroBonus(&gbonus);
  218. iw.text << VLC->generaltexth->advobtxt[120];
  219. iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
  220. }
  221. else
  222. {
  223. iw.text << VLC->generaltexth->advobtxt[33];
  224. iw.text.addReplacement(VLC->objh->creBanksNames[index]);
  225. }
  226. cb->showInfoDialog(&iw);
  227. }
  228. }
  229. void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
  230. {
  231. if (result.winner == 0)
  232. {
  233. int textID = -1;
  234. InfoWindow iw;
  235. iw.player = hero->getOwner();
  236. MetaString loot;
  237. switch (ID)
  238. {
  239. case Obj::CREATURE_BANK: case Obj::DRAGON_UTOPIA:
  240. textID = 34;
  241. break;
  242. case Obj::DERELICT_SHIP:
  243. if (multiplier)
  244. textID = 43;
  245. else
  246. {
  247. GiveBonus gbonus;
  248. gbonus.id = hero->id.getNum();
  249. gbonus.bonus.duration = Bonus::ONE_BATTLE;
  250. gbonus.bonus.source = Bonus::OBJECT;
  251. gbonus.bonus.sid = ID;
  252. gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[101];
  253. gbonus.bonus.type = Bonus::MORALE;
  254. gbonus.bonus.val = -1;
  255. cb->giveHeroBonus(&gbonus);
  256. textID = 42;
  257. iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
  258. }
  259. break;
  260. case Obj::CRYPT:
  261. if (bc->resources.size() != 0)
  262. textID = 121;
  263. else
  264. {
  265. iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
  266. GiveBonus gbonus;
  267. gbonus.id = hero->id.getNum();
  268. gbonus.bonus.duration = Bonus::ONE_BATTLE;
  269. gbonus.bonus.source = Bonus::OBJECT;
  270. gbonus.bonus.sid = ID;
  271. gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[ID];
  272. gbonus.bonus.type = Bonus::MORALE;
  273. gbonus.bonus.val = -1;
  274. cb->giveHeroBonus(&gbonus);
  275. textID = 120;
  276. iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
  277. }
  278. break;
  279. case Obj::SHIPWRECK:
  280. if (bc->resources.size())
  281. textID = 124;
  282. else
  283. textID = 123;
  284. break;
  285. }
  286. //grant resources
  287. if (textID != 42) //empty derelict ship gives no cash
  288. {
  289. for (int it = 0; it < bc->resources.size(); it++)
  290. {
  291. if (bc->resources[it] != 0)
  292. {
  293. iw.components.push_back (Component (Component::RESOURCE, it, bc->resources[it], 0));
  294. loot << "%d %s";
  295. loot.addReplacement(iw.components.back().val);
  296. loot.addReplacement(MetaString::RES_NAMES, iw.components.back().subtype);
  297. cb->giveResource (hero->getOwner(), static_cast<Res::ERes>(it), bc->resources[it]);
  298. }
  299. }
  300. }
  301. //grant artifacts
  302. for (auto & elem : artifacts)
  303. {
  304. iw.components.push_back (Component (Component::ARTIFACT, elem, 0, 0));
  305. loot << "%s";
  306. loot.addReplacement(MetaString::ART_NAMES, elem);
  307. cb->giveHeroNewArtifact (hero, VLC->arth->artifacts[elem], ArtifactPosition::FIRST_AVAILABLE);
  308. }
  309. //display loot
  310. if (!iw.components.empty())
  311. {
  312. iw.text.addTxt (MetaString::ADVOB_TXT, textID);
  313. if (textID == 34)
  314. {
  315. iw.text.addReplacement(MetaString::CRE_PL_NAMES, result.casualties[1].begin()->first);
  316. iw.text.addReplacement(loot.buildList());
  317. }
  318. cb->showInfoDialog(&iw);
  319. }
  320. loot.clear();
  321. iw.components.clear();
  322. iw.text.clear();
  323. //grant creatures
  324. CCreatureSet ourArmy;
  325. for (auto it = bc->creatures.cbegin(); it != bc->creatures.cend(); it++)
  326. {
  327. SlotID slot = ourArmy.getSlotFor(it->first);
  328. ourArmy.addToSlot(slot, it->first, it->second);
  329. }
  330. for (auto & elem : ourArmy.Slots())
  331. {
  332. iw.components.push_back(Component(*elem.second));
  333. loot << "%s";
  334. loot.addReplacement(*elem.second);
  335. }
  336. if (ourArmy.Slots().size())
  337. {
  338. if (ourArmy.Slots().size() == 1 && ourArmy.Slots().begin()->second->count == 1)
  339. iw.text.addTxt (MetaString::ADVOB_TXT, 185);
  340. else
  341. iw.text.addTxt (MetaString::ADVOB_TXT, 186);
  342. iw.text.addReplacement(loot.buildList());
  343. iw.text.addReplacement(hero->name);
  344. cb->showInfoDialog(&iw);
  345. cb->giveCreatures(this, hero, ourArmy, false);
  346. }
  347. cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0); //bc = nullptr
  348. }
  349. }
  350. void CBank::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
  351. {
  352. if (answer)
  353. {
  354. cb->startBattleI(hero, this, true);
  355. }
  356. }
  357. void CGPyramid::initObj()
  358. {
  359. std::vector<SpellID> available;
  360. cb->getAllowedSpells (available, 5);
  361. if (available.size())
  362. {
  363. bc = VLC->objh->banksInfo[21].front(); //TODO: remove hardcoded value?
  364. spell = *RandomGeneratorUtil::nextItem(available, cb->gameState()->getRandomGenerator());
  365. }
  366. else
  367. {
  368. logGlobal->errorStream() <<"No spells available for Pyramid! Object set to empty.";
  369. }
  370. setPropertyDer(ObjProperty::BANK_INIT_ARMY, cb->gameState()->getRandomGenerator().nextInt()); //set guards at game start
  371. }
  372. const std::string & CGPyramid::getHoverText() const
  373. {
  374. hoverName = VLC->objh->creBanksNames[21]+ " " + visitedTxt((bc==nullptr));
  375. return hoverName;
  376. }
  377. void CGPyramid::onHeroVisit (const CGHeroInstance * h) const
  378. {
  379. if (bc)
  380. {
  381. BlockingDialog bd (true, false);
  382. bd.player = h->getOwner();
  383. bd.soundID = soundBase::MYSTERY;
  384. bd.text << VLC->generaltexth->advobtxt[105];
  385. cb->showBlockingDialog(&bd);
  386. }
  387. else
  388. {
  389. InfoWindow iw;
  390. iw.player = h->getOwner();
  391. iw.text << VLC->generaltexth->advobtxt[107];
  392. iw.components.push_back (Component (Component::LUCK, 0 , -2, 0));
  393. GiveBonus gb;
  394. gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,-2,id.getNum(),VLC->generaltexth->arraytxt[70]);
  395. gb.id = h->id.getNum();
  396. cb->giveHeroBonus(&gb);
  397. cb->showInfoDialog(&iw);
  398. }
  399. }
  400. void CGPyramid::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
  401. {
  402. if (result.winner == 0)
  403. {
  404. InfoWindow iw;
  405. iw.player = hero->getOwner();
  406. iw.text.addTxt (MetaString::ADVOB_TXT, 106);
  407. iw.text.addTxt (MetaString::SPELL_NAME, spell);
  408. if (!hero->getArt(ArtifactPosition::SPELLBOOK))
  409. iw.text.addTxt (MetaString::ADVOB_TXT, 109); //no spellbook
  410. else if (hero->getSecSkillLevel(SecondarySkill::WISDOM) < 3)
  411. iw.text.addTxt (MetaString::ADVOB_TXT, 108); //no expert Wisdom
  412. else
  413. {
  414. std::set<SpellID> spells;
  415. spells.insert (SpellID(spell));
  416. cb->changeSpells (hero, true, spells);
  417. iw.components.push_back(Component (Component::SPELL, spell, 0, 0));
  418. }
  419. cb->showInfoDialog(&iw);
  420. cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0);
  421. }
  422. }