CWindowWithArtifacts.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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 "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/mapObjects/CGHeroInstance.h"
  28. #include "../../lib/networkPacks/ArtifactLocation.h"
  29. #include "../../lib/CConfigHandler.h"
  30. #include "../../CCallback.h"
  31. CWindowWithArtifacts::CWindowWithArtifacts(const std::vector<CArtifactsOfHeroPtr> * artSets)
  32. {
  33. if(artSets)
  34. this->artSets.insert(this->artSets.end(), artSets->begin(), artSets->end());
  35. }
  36. void CWindowWithArtifacts::addSet(CArtifactsOfHeroPtr artSet)
  37. {
  38. artSets.emplace_back(artSet);
  39. }
  40. void CWindowWithArtifacts::addSetAndCallbacks(CArtifactsOfHeroPtr artSet)
  41. {
  42. addSet(artSet);
  43. std::visit([this](auto artSetWeak)
  44. {
  45. auto artSet = artSetWeak.lock();
  46. artSet->clickPressedCallback = std::bind(&CWindowWithArtifacts::clickPressedArtPlaceHero, this, _1, _2, _3);
  47. artSet->showPopupCallback = std::bind(&CWindowWithArtifacts::showPopupArtPlaceHero, this, _1, _2, _3);
  48. artSet->gestureCallback = std::bind(&CWindowWithArtifacts::gestureArtPlaceHero, this, _1, _2, _3);
  49. }, artSet);
  50. }
  51. void CWindowWithArtifacts::addCloseCallback(CloseCallback callback)
  52. {
  53. closeCallback = callback;
  54. }
  55. const CGHeroInstance * CWindowWithArtifacts::getHeroPickedArtifact()
  56. {
  57. auto res = getState();
  58. if(res.has_value())
  59. return std::get<const CGHeroInstance*>(res.value());
  60. else
  61. return nullptr;
  62. }
  63. const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact()
  64. {
  65. auto res = getState();
  66. if(res.has_value())
  67. return std::get<const CArtifactInstance*>(res.value());
  68. else
  69. return nullptr;
  70. }
  71. void CWindowWithArtifacts::clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
  72. {
  73. const auto artSet = findAOHbyRef(artsInst);
  74. assert(artSet.has_value());
  75. if(artPlace.isLocked())
  76. return;
  77. std::visit(
  78. [this, &artPlace](auto artSetWeak) -> void
  79. {
  80. const auto artSetPtr = artSetWeak.lock();
  81. // Hero(Main, Exchange) window, Kingdom window, Altar window, Backpack window left click handler
  82. if constexpr(
  83. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> ||
  84. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>> ||
  85. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ||
  86. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
  87. {
  88. const auto pickedArtInst = getPickedArtifact();
  89. const auto heroPickedArt = getHeroPickedArtifact();
  90. const auto hero = artSetPtr->getHero();
  91. auto isTransferAllowed = false;
  92. std::string msg;
  93. if(pickedArtInst)
  94. {
  95. auto srcLoc = ArtifactLocation(heroPickedArt->id, ArtifactPosition::TRANSITION_POS);
  96. auto dstLoc = ArtifactLocation(hero->id, artPlace.slot);
  97. if(ArtifactUtils::isSlotBackpack(artPlace.slot))
  98. {
  99. if(pickedArtInst->artType->isBig())
  100. {
  101. // War machines cannot go to backpack
  102. msg = boost::str(boost::format(CGI->generaltexth->allTexts[153]) % pickedArtInst->artType->getNameTranslated());
  103. }
  104. else
  105. {
  106. if(ArtifactUtils::isBackpackFreeSlots(heroPickedArt))
  107. isTransferAllowed = true;
  108. else
  109. msg = CGI->generaltexth->translate("core.genrltxt.152");
  110. }
  111. }
  112. // Check if artifact transfer is possible
  113. else if(pickedArtInst->canBePutAt(hero, artPlace.slot, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID))
  114. {
  115. isTransferAllowed = true;
  116. }
  117. if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>>)
  118. {
  119. if(hero != heroPickedArt)
  120. isTransferAllowed = false;
  121. }
  122. if(isTransferAllowed)
  123. LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
  124. }
  125. else if(auto art = artPlace.getArt())
  126. {
  127. if(artSetPtr->getHero()->getOwner() == LOCPLINT->playerID)
  128. {
  129. if(checkSpecialArts(*art, hero, std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ? true : false))
  130. {
  131. assert(artSetPtr->getHero()->getSlotByInstance(art) != ArtifactPosition::PRE_FIRST);
  132. if(GH.isKeyboardCtrlDown())
  133. {
  134. std::shared_ptr<CArtifactsOfHeroMain> anotherHeroEquipmentPointer = nullptr;
  135. for(auto set : artSets)
  136. {
  137. if(std::holds_alternative<std::weak_ptr<CArtifactsOfHeroMain>>(set))
  138. {
  139. std::shared_ptr<CArtifactsOfHeroMain> heroEquipmentPointer = std::get<std::weak_ptr<CArtifactsOfHeroMain>>(set).lock();
  140. if(heroEquipmentPointer->getHero()->id != artSetPtr->getHero()->id)
  141. {
  142. anotherHeroEquipmentPointer = heroEquipmentPointer;
  143. break;
  144. }
  145. }
  146. }
  147. if(anotherHeroEquipmentPointer != nullptr)
  148. {
  149. ArtifactPosition availablePosition = ArtifactUtils::getArtAnyPosition(anotherHeroEquipmentPointer->getHero(), art->getTypeId());
  150. if(availablePosition != ArtifactPosition::PRE_FIRST)
  151. {
  152. LOCPLINT->cb->swapArtifacts(ArtifactLocation(artSetPtr->getHero()->id, artSetPtr->getHero()->getSlotByInstance(art)),
  153. ArtifactLocation(anotherHeroEquipmentPointer->getHero()->id, availablePosition));
  154. }
  155. }
  156. }
  157. else if(GH.isKeyboardAltDown())
  158. {
  159. ArtifactPosition destinationPosition = ArtifactPosition::PRE_FIRST;
  160. if(ArtifactUtils::isSlotEquipment(artPlace.slot))
  161. {
  162. ArtifactPosition availablePosition = ArtifactUtils::getArtBackpackPosition(artSetPtr->getHero(), art->getTypeId());
  163. if(availablePosition != ArtifactPosition::PRE_FIRST)
  164. {
  165. destinationPosition = availablePosition;
  166. }
  167. }
  168. else if(ArtifactUtils::isSlotBackpack(artPlace.slot))
  169. {
  170. ArtifactPosition availablePosition = ArtifactUtils::getArtAnyPosition(artSetPtr->getHero(), art->getTypeId());
  171. if(availablePosition != ArtifactPosition::PRE_FIRST && availablePosition != ArtifactPosition::BACKPACK_START)
  172. {
  173. destinationPosition = availablePosition;
  174. }
  175. }
  176. if(destinationPosition != ArtifactPosition::PRE_FIRST)
  177. {
  178. LOCPLINT->cb->swapArtifacts(ArtifactLocation(artSetPtr->getHero()->id, artPlace.slot),
  179. ArtifactLocation(artSetPtr->getHero()->id, destinationPosition));
  180. }
  181. }
  182. else
  183. {
  184. LOCPLINT->cb->swapArtifacts(ArtifactLocation(artSetPtr->getHero()->id, artPlace.slot),
  185. ArtifactLocation(artSetPtr->getHero()->id, ArtifactPosition::TRANSITION_POS));
  186. }
  187. }
  188. }
  189. else
  190. {
  191. for(const auto artSlot : ArtifactUtils::unmovableSlots())
  192. if(artPlace.slot == artSlot)
  193. {
  194. msg = CGI->generaltexth->allTexts[21];
  195. break;
  196. }
  197. }
  198. }
  199. if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
  200. {
  201. if(!isTransferAllowed && artPlace.getArt())
  202. {
  203. if(closeCallback)
  204. closeCallback();
  205. }
  206. }
  207. else
  208. {
  209. if(!msg.empty())
  210. LOCPLINT->showInfoDialog(msg);
  211. }
  212. }
  213. // Market window left click handler
  214. else if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>>)
  215. {
  216. if(artSetPtr->selectArtCallback && artPlace.getArt())
  217. {
  218. if(artPlace.getArt()->artType->isTradable())
  219. {
  220. artSetPtr->unmarkSlots();
  221. artPlace.selectSlot(true);
  222. artSetPtr->selectArtCallback(&artPlace);
  223. }
  224. else
  225. {
  226. // This item can't be traded
  227. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]);
  228. }
  229. }
  230. }
  231. else if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroQuickBackpack>>)
  232. {
  233. const auto hero = artSetPtr->getHero();
  234. LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero->id, artPlace.slot), ArtifactLocation(hero->id, artSetPtr->getFilterSlot()));
  235. if(closeCallback)
  236. closeCallback();
  237. }
  238. }, artSet.value());
  239. }
  240. void CWindowWithArtifacts::showPopupArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
  241. {
  242. const auto artSetWeak = findAOHbyRef(artsInst);
  243. assert(artSetWeak.has_value());
  244. if(artPlace.isLocked())
  245. return;
  246. std::visit(
  247. [&artPlace, &cursorPosition](auto artSetWeak) -> void
  248. {
  249. const auto artSetPtr = artSetWeak.lock();
  250. // Hero (Main, Exchange) window, Kingdom window, Backpack window right click handler
  251. if constexpr(
  252. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ||
  253. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> ||
  254. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>> ||
  255. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
  256. {
  257. if(artPlace.getArt())
  258. {
  259. if(ArtifactUtilsClient::askToDisassemble(artSetPtr->getHero(), artPlace.slot))
  260. {
  261. return;
  262. }
  263. if(ArtifactUtilsClient::askToAssemble(artSetPtr->getHero(), artPlace.slot))
  264. {
  265. return;
  266. }
  267. if(artPlace.text.size())
  268. artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
  269. }
  270. }
  271. // Altar window, Market window right click handler
  272. else if constexpr(
  273. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>> ||
  274. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroQuickBackpack>>)
  275. {
  276. if(artPlace.getArt() && artPlace.text.size())
  277. artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
  278. }
  279. }, artSetWeak.value());
  280. }
  281. void CWindowWithArtifacts::gestureArtPlaceHero(CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
  282. {
  283. const auto artSetWeak = findAOHbyRef(artsInst);
  284. assert(artSetWeak.has_value());
  285. if(artPlace.isLocked())
  286. return;
  287. std::visit(
  288. [&artPlace, cursorPosition](auto artSetWeak) -> void
  289. {
  290. const auto artSetPtr = artSetWeak.lock();
  291. if constexpr(
  292. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> ||
  293. std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>>)
  294. {
  295. if(!settings["general"]["enableUiEnhancements"].Bool())
  296. return;
  297. GH.windows().createAndPushWindow<CHeroQuickBackpackWindow>(artSetPtr->getHero(), artPlace.slot);
  298. auto backpackWindow = GH.windows().topWindow<CHeroQuickBackpackWindow>();
  299. backpackWindow->moveTo(cursorPosition - Point(1, 1));
  300. backpackWindow->fitToScreen(15);
  301. }
  302. }, artSetWeak.value());
  303. }
  304. void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
  305. {
  306. updateSlots();
  307. }
  308. void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
  309. {
  310. auto curState = getState();
  311. if(!curState.has_value())
  312. // Transition state. Nothing to do here. Just skip. Need to wait for final state.
  313. return;
  314. auto pickedArtInst = std::get<const CArtifactInstance*>(curState.value());
  315. auto artifactMovedBody = [this, withRedraw, &destLoc, &pickedArtInst](auto artSetWeak) -> void
  316. {
  317. auto artSetPtr = artSetWeak.lock();
  318. if(artSetPtr)
  319. {
  320. const auto hero = artSetPtr->getHero();
  321. if(pickedArtInst)
  322. {
  323. setCursorAnimation(*pickedArtInst);
  324. }
  325. else
  326. {
  327. artSetPtr->unmarkSlots();
  328. CCS->curh->dragAndDropCursor(nullptr);
  329. }
  330. if(withRedraw)
  331. {
  332. artSetPtr->updateWornSlots();
  333. artSetPtr->updateBackpackSlots();
  334. // Update arts bonuses on window.
  335. // TODO rework this part when CHeroWindow and CExchangeWindow are reworked
  336. if(auto * chw = dynamic_cast<CHeroWindow*>(this))
  337. {
  338. chw->update(hero, true);
  339. }
  340. else if(auto * cew = dynamic_cast<CExchangeWindow*>(this))
  341. {
  342. cew->updateWidgets();
  343. }
  344. artSetPtr->redraw();
  345. }
  346. // Make sure the status bar is updated so it does not display old text
  347. if(destLoc.artHolder == hero->id)
  348. {
  349. if(auto artPlace = artSetPtr->getArtPlace(destLoc.slot))
  350. artPlace->hover(true);
  351. }
  352. }
  353. };
  354. for(auto artSetWeak : artSets)
  355. std::visit(artifactMovedBody, artSetWeak);
  356. }
  357. void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc)
  358. {
  359. updateSlots();
  360. }
  361. void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc)
  362. {
  363. markPossibleSlots();
  364. updateSlots();
  365. }
  366. void CWindowWithArtifacts::updateSlots()
  367. {
  368. auto updateSlotBody = [](auto artSetWeak) -> void
  369. {
  370. if(const auto artSetPtr = artSetWeak.lock())
  371. {
  372. artSetPtr->updateWornSlots();
  373. artSetPtr->updateBackpackSlots();
  374. artSetPtr->redraw();
  375. }
  376. };
  377. for(auto artSetWeak : artSets)
  378. std::visit(updateSlotBody, artSetWeak);
  379. }
  380. std::optional<std::tuple<const CGHeroInstance*, const CArtifactInstance*>> CWindowWithArtifacts::getState()
  381. {
  382. const CArtifactInstance * artInst = nullptr;
  383. std::map<const CGHeroInstance*, size_t> pickedCnt;
  384. auto getHeroArtBody = [&artInst, &pickedCnt](auto artSetWeak) -> void
  385. {
  386. auto artSetPtr = artSetWeak.lock();
  387. if(artSetPtr)
  388. {
  389. if(const auto art = artSetPtr->getPickedArtifact())
  390. {
  391. const auto hero = artSetPtr->getHero();
  392. if(pickedCnt.count(hero) == 0)
  393. {
  394. pickedCnt.insert({ hero, hero->artifactsTransitionPos.size() });
  395. artInst = art;
  396. }
  397. }
  398. }
  399. };
  400. for(auto artSetWeak : artSets)
  401. std::visit(getHeroArtBody, artSetWeak);
  402. // The state is possible when the hero has placed an artifact in the ArtifactPosition::TRANSITION_POS,
  403. // and the previous artifact has not yet removed from the ArtifactPosition::TRANSITION_POS.
  404. // This is a transitional state. Then return nullopt.
  405. if(std::accumulate(std::begin(pickedCnt), std::end(pickedCnt), 0, [](size_t accum, const auto & value)
  406. {
  407. return accum + value.second;
  408. }) > 1)
  409. return std::nullopt;
  410. else
  411. return std::make_tuple(pickedCnt.begin()->first, artInst);
  412. }
  413. std::optional<CWindowWithArtifacts::CArtifactsOfHeroPtr> CWindowWithArtifacts::findAOHbyRef(CArtifactsOfHeroBase & artsInst)
  414. {
  415. std::optional<CArtifactsOfHeroPtr> res;
  416. auto findAOHBody = [&res, &artsInst](auto & artSetWeak) -> void
  417. {
  418. if(&artsInst == artSetWeak.lock().get())
  419. res = artSetWeak;
  420. };
  421. for(auto artSetWeak : artSets)
  422. {
  423. std::visit(findAOHBody, artSetWeak);
  424. if(res.has_value())
  425. return res;
  426. }
  427. return res;
  428. }
  429. void CWindowWithArtifacts::markPossibleSlots()
  430. {
  431. if(const auto pickedArtInst = getPickedArtifact())
  432. {
  433. const auto heroArtOwner = getHeroPickedArtifact();
  434. auto artifactAssembledBody = [&pickedArtInst, &heroArtOwner](auto artSetWeak) -> void
  435. {
  436. if(auto artSetPtr = artSetWeak.lock())
  437. {
  438. if(artSetPtr->isActive())
  439. {
  440. const auto hero = artSetPtr->getHero();
  441. if(heroArtOwner == hero || !std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>>)
  442. artSetPtr->markPossibleSlots(pickedArtInst, hero->tempOwner == LOCPLINT->playerID);
  443. }
  444. }
  445. };
  446. for(auto artSetWeak : artSets)
  447. std::visit(artifactAssembledBody, artSetWeak);
  448. }
  449. }
  450. bool CWindowWithArtifacts::checkSpecialArts(const CArtifactInstance & artInst, const CGHeroInstance * hero, bool isTrade)
  451. {
  452. const auto artId = artInst.getTypeId();
  453. if(artId == ArtifactID::SPELLBOOK)
  454. {
  455. GH.windows().createAndPushWindow<CSpellWindow>(hero, LOCPLINT, LOCPLINT->battleInt.get());
  456. return false;
  457. }
  458. if(artId == ArtifactID::CATAPULT)
  459. {
  460. // The Catapult must be equipped
  461. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312],
  462. std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT))));
  463. return false;
  464. }
  465. if(isTrade)
  466. {
  467. if(!artInst.artType->isTradable())
  468. {
  469. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21],
  470. std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, artId)));
  471. return false;
  472. }
  473. }
  474. return true;
  475. }
  476. void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst)
  477. {
  478. markPossibleSlots();
  479. if(artInst.isScroll() && settings["general"]["enableUiEnhancements"].Bool())
  480. {
  481. assert(artInst.getScrollSpellID().num >= 0);
  482. const auto animation = GH.renderHandler().loadAnimation(AnimationPath::builtin("spellscr"));
  483. animation->load(artInst.getScrollSpellID().num);
  484. CCS->curh->dragAndDropCursor(animation->getImage(artInst.getScrollSpellID().num)->scaleFast(Point(44, 34)));
  485. }
  486. else
  487. {
  488. CCS->curh->dragAndDropCursor(AnimationPath::builtin("artifact"), artInst.artType->getIconIndex());
  489. }
  490. }