CWindowWithArtifacts.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * CWindowWithArtifacts.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 "CWindowWithArtifacts.h"
  12. #include "../gui/CGuiHandler.h"
  13. #include "../gui/CursorHandler.h"
  14. #include "../gui/WindowHandler.h"
  15. #include "../render/IRenderHandler.h"
  16. #include "../render/CAnimation.h"
  17. #include "../render/IImage.h"
  18. #include "../widgets/CComponent.h"
  19. #include "../windows/CHeroWindow.h"
  20. #include "../windows/CSpellWindow.h"
  21. #include "../windows/GUIClasses.h"
  22. #include "../windows/CHeroBackpackWindow.h"
  23. #include "../CPlayerInterface.h"
  24. #include "../CGameInfo.h"
  25. #include "../../lib/ArtifactUtils.h"
  26. #include "../../lib/CGeneralTextHandler.h"
  27. #include "../../lib/networkPacks/ArtifactLocation.h"
  28. #include "../../lib/CConfigHandler.h"
  29. #include "../../CCallback.h"
  30. CWindowWithArtifacts::CWindowWithArtifacts(const std::vector<ArtifactsOfHeroVar> * artSets)
  31. {
  32. if(artSets)
  33. this->artSets.insert(this->artSets.end(), artSets->begin(), artSets->end());
  34. }
  35. void CWindowWithArtifacts::addSet(ArtifactsOfHeroVar newArtSet)
  36. {
  37. artSets.emplace_back(newArtSet);
  38. }
  39. void CWindowWithArtifacts::addSetAndCallbacks(ArtifactsOfHeroVar newArtSet)
  40. {
  41. addSet(newArtSet);
  42. std::visit([this](auto artSet)
  43. {
  44. if constexpr(std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroMarket>>)
  45. {
  46. artSet->clickPressedCallback = [artSet](CArtPlace & artPlace, const Point & cursorPosition)
  47. {
  48. artSet->onClickPrassedArtPlace(artPlace);
  49. };
  50. }
  51. if constexpr(std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroQuickBackpack>>)
  52. {
  53. artSet->clickPressedCallback = [this, artSet](CArtPlace & artPlace, const Point & cursorPosition)
  54. {
  55. if(const auto curHero = artSet->getHero())
  56. swapArtifactAndClose(*artSet, artPlace, ArtifactLocation(curHero->id, artSet->getFilterSlot()));
  57. };
  58. }
  59. if constexpr(
  60. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroMain>> ||
  61. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroKingdom>> ||
  62. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroAltar>> ||
  63. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroBackpack>>)
  64. {
  65. artSet->clickPressedCallback = [this, artSet](CArtPlace & artPlace, const Point & cursorPosition)
  66. {
  67. if(const auto curHero = artSet->getHero())
  68. clickPressedOnArtPlace(*curHero, artPlace.slot,
  69. !std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroKingdom>>,
  70. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroAltar>>,
  71. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroBackpack>>);
  72. };
  73. }
  74. if constexpr(
  75. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroMarket>> ||
  76. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroQuickBackpack>>)
  77. {
  78. artSet->showPopupCallback = [this](CArtPlace & artPlace, const Point & cursorPosition)
  79. {
  80. showArifactInfo(artPlace, cursorPosition);
  81. };
  82. }
  83. if constexpr(
  84. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroMain>> ||
  85. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroKingdom>> ||
  86. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroAltar>> ||
  87. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroBackpack>>)
  88. {
  89. artSet->showPopupCallback = [this, artSet](CArtPlace & artPlace, const Point & cursorPosition)
  90. {
  91. showArtifactAssembling(*artSet, artPlace, cursorPosition);
  92. };
  93. }
  94. if constexpr(
  95. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroMain>> ||
  96. std::is_same_v<decltype(artSet), std::shared_ptr<CArtifactsOfHeroKingdom>>)
  97. {
  98. artSet->gestureCallback = [this, artSet](CArtPlace & artPlace, const Point & cursorPosition)
  99. {
  100. showQuickBackpackWindow(*artSet, artPlace, cursorPosition);
  101. };
  102. }
  103. }, newArtSet);
  104. }
  105. void CWindowWithArtifacts::addCloseCallback(const CloseCallback & callback)
  106. {
  107. closeCallback = callback;
  108. }
  109. const CGHeroInstance * CWindowWithArtifacts::getHeroPickedArtifact()
  110. {
  111. const CGHeroInstance * hero = nullptr;
  112. for(auto & artSet : artSets)
  113. std::visit([&hero](auto artSetPtr)
  114. {
  115. if(const auto pickedArt = artSetPtr->getHero()->getArt(ArtifactPosition::TRANSITION_POS))
  116. {
  117. hero = artSetPtr->getHero();
  118. return;
  119. }
  120. }, artSet);
  121. return hero;
  122. }
  123. const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact()
  124. {
  125. const CArtifactInstance * art = nullptr;
  126. for(auto & artSet : artSets)
  127. std::visit([&art](auto artSetPtr)
  128. {
  129. if(const auto pickedArt = artSetPtr->getHero()->getArt(ArtifactPosition::TRANSITION_POS))
  130. {
  131. art = pickedArt;
  132. return;
  133. }
  134. }, artSet);
  135. return art;
  136. }
  137. void CWindowWithArtifacts::clickPressedOnArtPlace(const CGHeroInstance & hero, const ArtifactPosition & slot,
  138. bool allowExchange, bool altarTrading, bool closeWindow)
  139. {
  140. if(!LOCPLINT->makingTurn)
  141. return;
  142. if(const auto heroArtOwner = getHeroPickedArtifact())
  143. {
  144. if(allowExchange || hero.id == heroArtOwner->id)
  145. putPickedArtifact(hero, slot);
  146. }
  147. else if(auto art = hero.getArt(slot))
  148. {
  149. if(hero.getOwner() == LOCPLINT->playerID)
  150. {
  151. if(checkSpecialArts(*art, hero, altarTrading))
  152. {
  153. assert(hero.getArt(slot));
  154. auto srcLoc = ArtifactLocation(hero.id, slot);
  155. auto dstLoc = ArtifactLocation(hero.id, ArtifactPosition::TRANSITION_POS);
  156. if(GH.isKeyboardCtrlDown())
  157. {
  158. for(auto & anotherSet : artSets)
  159. if(std::holds_alternative<std::shared_ptr<CArtifactsOfHeroMain>>(anotherSet))
  160. {
  161. const auto anotherHeroEquipment = std::get<std::shared_ptr<CArtifactsOfHeroMain>>(anotherSet);
  162. if(hero.id != anotherHeroEquipment->getHero()->id)
  163. {
  164. dstLoc.slot = ArtifactPosition::FIRST_AVAILABLE;
  165. dstLoc.artHolder = anotherHeroEquipment->getHero()->id;
  166. break;
  167. }
  168. }
  169. }
  170. else if(GH.isKeyboardAltDown())
  171. {
  172. const auto artId = hero.getArt(slot)->getTypeId();
  173. if(ArtifactUtils::isSlotEquipment(slot))
  174. dstLoc.slot = ArtifactUtils::getArtBackpackPosition(&hero, artId);
  175. else if(ArtifactUtils::isSlotBackpack(slot))
  176. dstLoc.slot = ArtifactUtils::getArtEquippedPosition(&hero, artId);
  177. }
  178. else if(closeWindow && closeCallback)
  179. {
  180. closeCallback();
  181. }
  182. if(dstLoc.slot != ArtifactPosition::PRE_FIRST)
  183. LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
  184. }
  185. }
  186. else
  187. {
  188. for(const auto & artSlot : ArtifactUtils::unmovableSlots())
  189. if(slot == artSlot)
  190. {
  191. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]);
  192. break;
  193. }
  194. }
  195. }
  196. }
  197. void CWindowWithArtifacts::swapArtifactAndClose(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const ArtifactLocation & dstLoc)
  198. {
  199. LOCPLINT->cb->swapArtifacts(ArtifactLocation(artsInst.getHero()->id, artPlace.slot), dstLoc);
  200. if(closeCallback)
  201. closeCallback();
  202. }
  203. void CWindowWithArtifacts::showArtifactAssembling(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
  204. {
  205. if(artPlace.getArt())
  206. {
  207. if(ArtifactUtilsClient::askToDisassemble(artsInst.getHero(), artPlace.slot))
  208. return;
  209. if(ArtifactUtilsClient::askToAssemble(artsInst.getHero(), artPlace.slot))
  210. return;
  211. if(artPlace.text.size())
  212. artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
  213. }
  214. }
  215. void CWindowWithArtifacts::showArifactInfo(CArtPlace & artPlace, const Point & cursorPosition)
  216. {
  217. if(artPlace.getArt() && artPlace.text.size())
  218. artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
  219. }
  220. void CWindowWithArtifacts::showQuickBackpackWindow(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
  221. {
  222. if(!settings["general"]["enableUiEnhancements"].Bool())
  223. return;
  224. GH.windows().createAndPushWindow<CHeroQuickBackpackWindow>(artsInst.getHero(), artPlace.slot);
  225. auto backpackWindow = GH.windows().topWindow<CHeroQuickBackpackWindow>();
  226. backpackWindow->moveTo(cursorPosition - Point(1, 1));
  227. backpackWindow->fitToScreen(15);
  228. }
  229. void CWindowWithArtifacts::activate()
  230. {
  231. if(const auto art = getPickedArtifact())
  232. setCursorAnimation(*art);
  233. CWindowObject::activate();
  234. }
  235. void CWindowWithArtifacts::deactivate()
  236. {
  237. CCS->curh->dragAndDropCursor(nullptr);
  238. CWindowObject::deactivate();
  239. }
  240. void CWindowWithArtifacts::enableArtifactsCostumeSwitcher() const
  241. {
  242. for(auto & artSet : artSets)
  243. std::visit(
  244. [](auto artSetPtr)
  245. {
  246. if constexpr(std::is_same_v<decltype(artSetPtr), std::shared_ptr<CArtifactsOfHeroMain>>)
  247. artSetPtr->enableArtifactsCostumeSwitcher();
  248. }, artSet);
  249. }
  250. void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
  251. {
  252. update();
  253. }
  254. void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
  255. {
  256. auto artifactMovedBody = [this](auto artSetPtr)
  257. {
  258. if(const auto pickedArtInst = getPickedArtifact())
  259. {
  260. setCursorAnimation(*pickedArtInst);
  261. }
  262. else
  263. {
  264. artSetPtr->unmarkSlots();
  265. CCS->curh->dragAndDropCursor(nullptr);
  266. }
  267. };
  268. for(auto & artSet : artSets)
  269. std::visit(artifactMovedBody, artSet);
  270. if(withRedraw)
  271. update();
  272. }
  273. void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc)
  274. {
  275. update();
  276. }
  277. void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc)
  278. {
  279. markPossibleSlots();
  280. update();
  281. }
  282. void CWindowWithArtifacts::update()
  283. {
  284. for(auto & artSet : artSets)
  285. std::visit([this](auto artSetPtr)
  286. {
  287. artSetPtr->updateWornSlots();
  288. artSetPtr->updateBackpackSlots();
  289. // Update arts bonuses on window.
  290. // TODO rework this part when CHeroWindow and CExchangeWindow are reworked
  291. if(auto * chw = dynamic_cast<CHeroWindow*>(this))
  292. {
  293. chw->update(artSetPtr->getHero(), true);
  294. }
  295. else if(auto * cew = dynamic_cast<CExchangeWindow*>(this))
  296. {
  297. cew->updateWidgets();
  298. }
  299. // Make sure the status bar is updated so it does not display old text
  300. if(auto artPlace = artSetPtr->getArtPlace(GH.getCursorPosition()))
  301. artPlace->hover(true);
  302. artSetPtr->redraw();
  303. }, artSet);
  304. }
  305. void CWindowWithArtifacts::markPossibleSlots()
  306. {
  307. if(const auto pickedArtInst = getPickedArtifact())
  308. {
  309. const auto heroArtOwner = getHeroPickedArtifact();
  310. auto artifactAssembledBody = [&pickedArtInst, &heroArtOwner](auto artSetPtr)
  311. {
  312. if(artSetPtr->isActive())
  313. {
  314. const auto hero = artSetPtr->getHero();
  315. if(heroArtOwner == hero || !std::is_same_v<decltype(artSetPtr), std::shared_ptr<CArtifactsOfHeroKingdom>>)
  316. artSetPtr->markPossibleSlots(pickedArtInst, hero->tempOwner == LOCPLINT->playerID);
  317. }
  318. };
  319. for(auto & artSet : artSets)
  320. std::visit(artifactAssembledBody, artSet);
  321. }
  322. }
  323. bool CWindowWithArtifacts::checkSpecialArts(const CArtifactInstance & artInst, const CGHeroInstance & hero, bool isTrade) const
  324. {
  325. const auto artId = artInst.getTypeId();
  326. if(artId == ArtifactID::SPELLBOOK)
  327. {
  328. GH.windows().createAndPushWindow<CSpellWindow>(&hero, LOCPLINT, LOCPLINT->battleInt.get());
  329. return false;
  330. }
  331. if(artId == ArtifactID::CATAPULT)
  332. {
  333. // The Catapult must be equipped
  334. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312],
  335. std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT))));
  336. return false;
  337. }
  338. if(isTrade && !artInst.artType->isTradable())
  339. {
  340. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21],
  341. std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, artId)));
  342. return false;
  343. }
  344. return true;
  345. }
  346. void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst)
  347. {
  348. markPossibleSlots();
  349. if(artInst.isScroll() && settings["general"]["enableUiEnhancements"].Bool())
  350. {
  351. assert(artInst.getScrollSpellID().num >= 0);
  352. const auto animation = GH.renderHandler().loadAnimation(AnimationPath::builtin("spellscr"));
  353. animation->load(artInst.getScrollSpellID().num);
  354. CCS->curh->dragAndDropCursor(animation->getImage(artInst.getScrollSpellID().num)->scaleFast(Point(44, 34)));
  355. }
  356. else
  357. {
  358. CCS->curh->dragAndDropCursor(AnimationPath::builtin("artifact"), artInst.artType->getIconIndex());
  359. }
  360. }
  361. void CWindowWithArtifacts::putPickedArtifact(const CGHeroInstance & curHero, const ArtifactPosition & targetSlot)
  362. {
  363. const auto heroArtOwner = getHeroPickedArtifact();
  364. const auto pickedArt = getPickedArtifact();
  365. auto srcLoc = ArtifactLocation(heroArtOwner->id, ArtifactPosition::TRANSITION_POS);
  366. auto dstLoc = ArtifactLocation(curHero.id, targetSlot);
  367. if(ArtifactUtils::isSlotBackpack(dstLoc.slot))
  368. {
  369. if(pickedArt->artType->isBig())
  370. {
  371. // War machines cannot go to backpack
  372. LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % pickedArt->artType->getNameTranslated()));
  373. }
  374. else
  375. {
  376. if(ArtifactUtils::isBackpackFreeSlots(heroArtOwner))
  377. LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
  378. else
  379. LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.152"));
  380. }
  381. }
  382. // Check if artifact transfer is possible
  383. else if(pickedArt->canBePutAt(&curHero, dstLoc.slot, true) && (!curHero.getArt(targetSlot) || curHero.tempOwner == LOCPLINT->playerID))
  384. {
  385. LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
  386. }
  387. }