CBank.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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 <vcmi/spells/Spell.h>
  13. #include <vcmi/spells/Service.h>
  14. #include "../NetPacks.h"
  15. #include "../CGeneralTextHandler.h"
  16. #include "../CSoundBase.h"
  17. #include "../GameSettings.h"
  18. #include "../mapObjectConstructors/CObjectClassesHandler.h"
  19. #include "../mapObjectConstructors/CBankInstanceConstructor.h"
  20. #include "../IGameCallback.h"
  21. #include "../gameState/CGameState.h"
  22. VCMI_LIB_NAMESPACE_BEGIN
  23. ///helpers
  24. static std::string visitedTxt(const bool visited)
  25. {
  26. int id = visited ? 352 : 353;
  27. return VLC->generaltexth->allTexts[id];
  28. }
  29. //must be instantiated in .cpp file for access to complete types of all member fields
  30. CBank::CBank() = default;
  31. //must be instantiated in .cpp file for access to complete types of all member fields
  32. CBank::~CBank() = default;
  33. void CBank::initObj(CRandomGenerator & rand)
  34. {
  35. daycounter = 0;
  36. resetDuration = 0;
  37. VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand);
  38. }
  39. bool CBank::isCoastVisitable() const
  40. {
  41. return coastVisitable;
  42. }
  43. std::string CBank::getHoverText(PlayerColor player) const
  44. {
  45. // TODO: record visited players
  46. return getObjectName() + " " + visitedTxt(bc == nullptr);
  47. }
  48. void CBank::setConfig(const BankConfig & config)
  49. {
  50. bc = std::make_unique<BankConfig>(config);
  51. clearSlots(); // remove all stacks, if any
  52. for(const auto & stack : config.guards)
  53. setCreature (SlotID(stacksCount()), stack.type->getId(), stack.count);
  54. }
  55. void CBank::setPropertyDer (ui8 what, ui32 val)
  56. {
  57. switch (what)
  58. {
  59. case ObjProperty::BANK_DAYCOUNTER: //daycounter
  60. daycounter+=val;
  61. break;
  62. case ObjProperty::BANK_RESET:
  63. // FIXME: Object reset must be done by separate netpack from server
  64. initObj(cb->gameState()->getRandomGenerator());
  65. daycounter = 1; //yes, 1 since "today" daycounter won't be incremented
  66. break;
  67. case ObjProperty::BANK_CLEAR:
  68. bc.reset();
  69. break;
  70. }
  71. }
  72. void CBank::newTurn(CRandomGenerator & rand) const
  73. {
  74. if (bc == nullptr)
  75. {
  76. if (resetDuration != 0)
  77. {
  78. if (daycounter >= resetDuration)
  79. cb->setObjProperty (id, ObjProperty::BANK_RESET, 0); //daycounter 0
  80. else
  81. cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
  82. }
  83. }
  84. }
  85. bool CBank::wasVisited (PlayerColor player) const
  86. {
  87. return !bc; //FIXME: player A should not know about visit done by player B
  88. }
  89. void CBank::onHeroVisit(const CGHeroInstance * h) const
  90. {
  91. int banktext = 0;
  92. switch (ID)
  93. {
  94. case Obj::DERELICT_SHIP:
  95. banktext = 41;
  96. break;
  97. case Obj::DRAGON_UTOPIA:
  98. banktext = 47;
  99. break;
  100. case Obj::CRYPT:
  101. banktext = 119;
  102. break;
  103. case Obj::SHIPWRECK:
  104. banktext = 122;
  105. break;
  106. case Obj::PYRAMID:
  107. banktext = 105;
  108. break;
  109. case Obj::CREATURE_BANK:
  110. default:
  111. banktext = 32;
  112. break;
  113. }
  114. BlockingDialog bd(true, false);
  115. bd.player = h->getOwner();
  116. bd.soundID = soundBase::invalid; // Sound is handled in json files, else two sounds are played
  117. bd.text.appendLocalString(EMetaText::ADVOB_TXT, banktext);
  118. if (banktext == 32)
  119. bd.text.replaceRawString(getObjectName());
  120. if (VLC->settings()->getBoolean(EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION))
  121. {
  122. std::map<CreatureID, int> guardsAmounts;
  123. for (auto const & slot : Slots())
  124. if (slot.second)
  125. guardsAmounts[slot.second->getCreatureID()] += slot.second->getCount();
  126. for (auto const & guard : guardsAmounts)
  127. {
  128. Component comp;
  129. comp.id = Component::EComponentType::CREATURE;
  130. comp.subtype = guard.first.getNum();
  131. comp.val = guard.second;
  132. bd.components.push_back(comp);
  133. }
  134. }
  135. cb->showBlockingDialog(&bd);
  136. }
  137. void CBank::doVisit(const CGHeroInstance * hero) const
  138. {
  139. int textID = -1;
  140. InfoWindow iw;
  141. iw.type = EInfoWindowMode::AUTO;
  142. iw.player = hero->getOwner();
  143. MetaString loot;
  144. if (bc)
  145. {
  146. switch (ID)
  147. {
  148. case Obj::DERELICT_SHIP:
  149. textID = 43;
  150. break;
  151. case Obj::CRYPT:
  152. textID = 121;
  153. break;
  154. case Obj::SHIPWRECK:
  155. textID = 124;
  156. break;
  157. case Obj::PYRAMID:
  158. textID = 106;
  159. break;
  160. case Obj::CREATURE_BANK:
  161. case Obj::DRAGON_UTOPIA:
  162. default:
  163. textID = 34;
  164. break;
  165. }
  166. }
  167. else
  168. {
  169. switch (ID)
  170. {
  171. case Obj::SHIPWRECK:
  172. case Obj::DERELICT_SHIP:
  173. case Obj::CRYPT:
  174. {
  175. GiveBonus gbonus;
  176. gbonus.id = hero->id.getNum();
  177. gbonus.bonus.duration = BonusDuration::ONE_BATTLE;
  178. gbonus.bonus.source = BonusSource::OBJECT;
  179. gbonus.bonus.sid = ID;
  180. gbonus.bonus.type = BonusType::MORALE;
  181. gbonus.bonus.val = -1;
  182. switch (ID)
  183. {
  184. case Obj::SHIPWRECK:
  185. textID = 123;
  186. gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[99]);
  187. break;
  188. case Obj::DERELICT_SHIP:
  189. textID = 42;
  190. gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[101]);
  191. break;
  192. case Obj::CRYPT:
  193. textID = 120;
  194. gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[98]);
  195. break;
  196. }
  197. cb->giveHeroBonus(&gbonus);
  198. iw.components.emplace_back(Component::EComponentType::MORALE, 0, -1, 0);
  199. iw.soundID = soundBase::GRAVEYARD;
  200. break;
  201. }
  202. case Obj::PYRAMID:
  203. {
  204. GiveBonus gb;
  205. gb.bonus = Bonus(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT, -2, id.getNum(), VLC->generaltexth->arraytxt[70]);
  206. gb.id = hero->id.getNum();
  207. cb->giveHeroBonus(&gb);
  208. textID = 107;
  209. iw.components.emplace_back(Component::EComponentType::LUCK, 0, -2, 0);
  210. break;
  211. }
  212. case Obj::CREATURE_BANK:
  213. case Obj::DRAGON_UTOPIA:
  214. default:
  215. iw.text.appendRawString(VLC->generaltexth->advobtxt[33]);// This was X, now is completely empty
  216. iw.text.replaceRawString(getObjectName());
  217. }
  218. if(textID != -1)
  219. {
  220. iw.text.appendLocalString(EMetaText::ADVOB_TXT, textID);
  221. }
  222. cb->showInfoDialog(&iw);
  223. }
  224. //grant resources
  225. if (bc)
  226. {
  227. for (int it = 0; it < bc->resources.size(); it++)
  228. {
  229. if (bc->resources[it] != 0)
  230. {
  231. iw.components.emplace_back(Component::EComponentType::RESOURCE, it, bc->resources[it], 0);
  232. loot.appendRawString("%d %s");
  233. loot.replaceNumber(iw.components.back().val);
  234. loot.replaceLocalString(EMetaText::RES_NAMES, iw.components.back().subtype);
  235. cb->giveResource(hero->getOwner(), static_cast<EGameResID>(it), bc->resources[it]);
  236. }
  237. }
  238. //grant artifacts
  239. for (auto & elem : bc->artifacts)
  240. {
  241. iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0);
  242. loot.appendRawString("%s");
  243. loot.replaceLocalString(EMetaText::ART_NAMES, elem);
  244. cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE);
  245. }
  246. //display loot
  247. if (!iw.components.empty())
  248. {
  249. iw.text.appendLocalString(EMetaText::ADVOB_TXT, textID);
  250. if (textID == 34)
  251. {
  252. const auto * strongest = boost::range::max_element(bc->guards, [](const CStackBasicDescriptor & a, const CStackBasicDescriptor & b)
  253. {
  254. return a.type->getFightValue() < b.type->getFightValue();
  255. })->type;
  256. iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, strongest->getId());
  257. iw.text.replaceRawString(loot.buildList());
  258. }
  259. cb->showInfoDialog(&iw);
  260. }
  261. loot.clear();
  262. iw.components.clear();
  263. iw.text.clear();
  264. if (!bc->spells.empty())
  265. {
  266. std::set<SpellID> spells;
  267. bool noWisdom = false;
  268. if(textID == 106)
  269. {
  270. iw.text.appendLocalString(EMetaText::ADVOB_TXT, textID); //pyramid
  271. }
  272. for(const SpellID & spellId : bc->spells)
  273. {
  274. const auto * spell = spellId.toSpell(VLC->spells());
  275. iw.text.appendLocalString(EMetaText::SPELL_NAME, spellId);
  276. if(spell->getLevel() <= hero->maxSpellLevel())
  277. {
  278. if(hero->canLearnSpell(spell))
  279. {
  280. spells.insert(spellId);
  281. iw.components.emplace_back(Component::EComponentType::SPELL, spellId, 0, 0);
  282. }
  283. }
  284. else
  285. noWisdom = true;
  286. }
  287. if (!hero->getArt(ArtifactPosition::SPELLBOOK))
  288. iw.text.appendLocalString(EMetaText::ADVOB_TXT, 109); //no spellbook
  289. else if(noWisdom)
  290. iw.text.appendLocalString(EMetaText::ADVOB_TXT, 108); //no expert Wisdom
  291. if(!iw.components.empty() || !iw.text.toString().empty())
  292. cb->showInfoDialog(&iw);
  293. if(!spells.empty())
  294. cb->changeSpells(hero, true, spells);
  295. }
  296. iw.components.clear();
  297. iw.text.clear();
  298. //grant creatures
  299. CCreatureSet ourArmy;
  300. for(const auto & slot : bc->creatures)
  301. {
  302. ourArmy.addToSlot(ourArmy.getSlotFor(slot.type->getId()), slot.type->getId(), slot.count);
  303. }
  304. for(const auto & elem : ourArmy.Slots())
  305. {
  306. iw.components.emplace_back(*elem.second);
  307. loot.appendRawString("%s");
  308. loot.replaceCreatureName(*elem.second);
  309. }
  310. if(ourArmy.stacksCount())
  311. {
  312. if(ourArmy.stacksCount() == 1 && ourArmy.Slots().begin()->second->count == 1)
  313. iw.text.appendLocalString(EMetaText::ADVOB_TXT, 185);
  314. else
  315. iw.text.appendLocalString(EMetaText::ADVOB_TXT, 186);
  316. iw.text.replaceRawString(loot.buildList());
  317. iw.text.replaceRawString(hero->getNameTranslated());
  318. cb->showInfoDialog(&iw);
  319. cb->giveCreatures(this, hero, ourArmy, false);
  320. }
  321. cb->setObjProperty(id, ObjProperty::BANK_CLEAR, 0); //bc = nullptr
  322. }
  323. }
  324. void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
  325. {
  326. if (result.winner == 0)
  327. {
  328. doVisit(hero);
  329. }
  330. }
  331. void CBank::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
  332. {
  333. if (answer)
  334. {
  335. if (bc) // not looted bank
  336. cb->startBattleI(hero, this, true);
  337. else
  338. doVisit(hero);
  339. }
  340. }
  341. VCMI_LIB_NAMESPACE_END