CExchangeWindow.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /*
  2. * CExchangeWindow.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 "CExchangeWindow.h"
  12. #include "CHeroBackpackWindow.h"
  13. #include "../CGameInfo.h"
  14. #include "../CPlayerInterface.h"
  15. #include "../gui/CGuiHandler.h"
  16. #include "../gui/CursorHandler.h"
  17. #include "../gui/Shortcut.h"
  18. #include "../gui/WindowHandler.h"
  19. #include "../widgets/CGarrisonInt.h"
  20. #include "../widgets/Images.h"
  21. #include "../widgets/Buttons.h"
  22. #include "../widgets/TextControls.h"
  23. #include "../render/IRenderHandler.h"
  24. #include "../render/CAnimation.h"
  25. #include "../../CCallback.h"
  26. #include "../lib/mapObjects/CGHeroInstance.h"
  27. #include "../lib/CGeneralTextHandler.h"
  28. #include "../lib/CHeroHandler.h"
  29. #include "../lib/filesystem/Filesystem.h"
  30. #include "../lib/CSkillHandler.h"
  31. #include "../lib/TextOperations.h"
  32. static const std::string QUICK_EXCHANGE_BG = "quick-exchange/TRADEQE";
  33. static bool isQuickExchangeLayoutAvailable()
  34. {
  35. return CResourceHandler::get()->existsResource(ImagePath::builtin("SPRITES/" + QUICK_EXCHANGE_BG));
  36. }
  37. CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID)
  38. : CWindowObject(PLAYER_COLORED | BORDERED, ImagePath::builtin(isQuickExchangeLayoutAvailable() ? QUICK_EXCHANGE_BG : "TRADE2")),
  39. controller(hero1, hero2)
  40. {
  41. const bool qeLayout = isQuickExchangeLayoutAvailable();
  42. OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
  43. addUsedEvents(KEYBOARD);
  44. heroInst[0] = LOCPLINT->cb->getHero(hero1);
  45. heroInst[1] = LOCPLINT->cb->getHero(hero2);
  46. auto genTitle = [](const CGHeroInstance * h)
  47. {
  48. boost::format fmt(CGI->generaltexth->allTexts[138]);
  49. fmt % h->getNameTranslated() % h->level % h->getClassNameTranslated();
  50. return boost::str(fmt);
  51. };
  52. titles[0] = std::make_shared<CLabel>(147, 25, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, genTitle(heroInst[0]));
  53. titles[1] = std::make_shared<CLabel>(653, 25, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, genTitle(heroInst[1]));
  54. auto PSKIL32 = GH.renderHandler().loadAnimation(AnimationPath::builtin("PSKIL32"));
  55. PSKIL32->preload();
  56. auto SECSK32 = GH.renderHandler().loadAnimation(AnimationPath::builtin("SECSK32"));
  57. for(int g = 0; g < 4; ++g)
  58. {
  59. if (qeLayout)
  60. primSkillImages.push_back(std::make_shared<CAnimImage>(PSKIL32, g, Rect(389, 12 + 26 * g, 22, 22)));
  61. else
  62. primSkillImages.push_back(std::make_shared<CAnimImage>(PSKIL32, g, 0, 385, 19 + 36 * g));
  63. }
  64. for(int leftRight : {0, 1})
  65. {
  66. const CGHeroInstance * hero = heroInst.at(leftRight);
  67. for(int m=0; m<GameConstants::PRIMARY_SKILLS; ++m)
  68. primSkillValues[leftRight].push_back(std::make_shared<CLabel>(352 + (qeLayout ? 96 : 93) * leftRight, (qeLayout ? 22 : 35) + (qeLayout ? 26 : 36) * m, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE));
  69. for(int m=0; m < hero->secSkills.size(); ++m)
  70. secSkillIcons[leftRight].push_back(std::make_shared<CAnimImage>(SECSK32, 0, 0, 32 + 36 * m + 454 * leftRight, qeLayout ? 83 : 88));
  71. specImages[leftRight] = std::make_shared<CAnimImage>(AnimationPath::builtin("UN32"), hero->type->imageIndex, 0, 67 + 490 * leftRight, qeLayout ? 41 : 45);
  72. expImages[leftRight] = std::make_shared<CAnimImage>(PSKIL32, 4, 0, 103 + 490 * leftRight, qeLayout ? 41 : 45);
  73. expValues[leftRight] = std::make_shared<CLabel>(119 + 490 * leftRight, qeLayout ? 66 : 71, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
  74. manaImages[leftRight] = std::make_shared<CAnimImage>(PSKIL32, 5, 0, 139 + 490 * leftRight, qeLayout ? 41 : 45);
  75. manaValues[leftRight] = std::make_shared<CLabel>(155 + 490 * leftRight, qeLayout ? 66 : 71, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
  76. }
  77. artifs[0] = std::make_shared<CArtifactsOfHeroMain>(Point(-334, 151));
  78. artifs[0]->setHero(heroInst[0]);
  79. artifs[1] = std::make_shared<CArtifactsOfHeroMain>(Point(98, 151));
  80. artifs[1]->setHero(heroInst[1]);
  81. addSetAndCallbacks(artifs[0]);
  82. addSetAndCallbacks(artifs[1]);
  83. for(int g=0; g<4; ++g)
  84. {
  85. primSkillAreas.push_back(std::make_shared<LRClickableAreaWTextComp>());
  86. if (qeLayout)
  87. primSkillAreas[g]->pos = Rect(Point(pos.x + 324, pos.y + 12 + 26 * g), Point(152, 22));
  88. else
  89. primSkillAreas[g]->pos = Rect(Point(pos.x + 329, pos.y + 19 + 36 * g), Point(140, 32));
  90. primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g];
  91. primSkillAreas[g]->component = Component( ComponentType::PRIM_SKILL, PrimarySkill(g));
  92. primSkillAreas[g]->hoverText = CGI->generaltexth->heroscrn[1];
  93. boost::replace_first(primSkillAreas[g]->hoverText, "%s", CGI->generaltexth->primarySkillNames[g]);
  94. }
  95. //heroes related thing
  96. for(int b=0; b < heroInst.size(); b++)
  97. {
  98. const CGHeroInstance * hero = heroInst.at(b);
  99. //secondary skill's clickable areas
  100. for(int g=0; g<hero->secSkills.size(); ++g)
  101. {
  102. SecondarySkill skill = hero->secSkills[g].first;
  103. int level = hero->secSkills[g].second; // <1, 3>
  104. secSkillAreas[b].push_back(std::make_shared<LRClickableAreaWTextComp>());
  105. secSkillAreas[b][g]->pos = Rect(Point(pos.x + 32 + g * 36 + b * 454 , pos.y + (qeLayout ? 83 : 88)), Point(32, 32) );
  106. secSkillAreas[b][g]->component = Component(ComponentType::SEC_SKILL, skill, level);
  107. secSkillAreas[b][g]->text = CGI->skillh->getByIndex(skill)->getDescriptionTranslated(level);
  108. secSkillAreas[b][g]->hoverText = CGI->generaltexth->heroscrn[21];
  109. boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->generaltexth->levels[level - 1]);
  110. boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->skillh->getByIndex(skill)->getNameTranslated());
  111. }
  112. heroAreas[b] = std::make_shared<CHeroArea>(257 + 228 * b, 13, hero);
  113. heroAreas[b]->addClickCallback([this, hero]() -> void
  114. {
  115. if(getPickedArtifact() == nullptr)
  116. LOCPLINT->openHeroWindow(hero);
  117. });
  118. specialtyAreas[b] = std::make_shared<LRClickableAreaWText>();
  119. specialtyAreas[b]->pos = Rect(Point(pos.x + 69 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));
  120. specialtyAreas[b]->hoverText = CGI->generaltexth->heroscrn[27];
  121. specialtyAreas[b]->text = hero->type->getSpecialtyDescriptionTranslated();
  122. experienceAreas[b] = std::make_shared<LRClickableAreaWText>();
  123. experienceAreas[b]->pos = Rect(Point(pos.x + 105 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));
  124. experienceAreas[b]->hoverText = CGI->generaltexth->heroscrn[9];
  125. experienceAreas[b]->text = CGI->generaltexth->allTexts[2];
  126. boost::algorithm::replace_first(experienceAreas[b]->text, "%d", std::to_string(hero->level));
  127. boost::algorithm::replace_first(experienceAreas[b]->text, "%d", std::to_string(CGI->heroh->reqExp(hero->level+1)));
  128. boost::algorithm::replace_first(experienceAreas[b]->text, "%d", std::to_string(hero->exp));
  129. spellPointsAreas[b] = std::make_shared<LRClickableAreaWText>();
  130. spellPointsAreas[b]->pos = Rect(Point(pos.x + 141 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));
  131. spellPointsAreas[b]->hoverText = CGI->generaltexth->heroscrn[22];
  132. spellPointsAreas[b]->text = CGI->generaltexth->allTexts[205];
  133. boost::algorithm::replace_first(spellPointsAreas[b]->text, "%s", hero->getNameTranslated());
  134. boost::algorithm::replace_first(spellPointsAreas[b]->text, "%d", std::to_string(hero->mana));
  135. boost::algorithm::replace_first(spellPointsAreas[b]->text, "%d", std::to_string(hero->manaLimit()));
  136. morale[b] = std::make_shared<MoraleLuckBox>(true, Rect(Point(176 + 490 * b, 39), Point(32, 32)), true);
  137. luck[b] = std::make_shared<MoraleLuckBox>(false, Rect(Point(212 + 490 * b, 39), Point(32, 32)), true);
  138. }
  139. quit = std::make_shared<CButton>(Point(732, 567), AnimationPath::builtin("IOKAY.DEF"), CGI->generaltexth->zelp[600], std::bind(&CExchangeWindow::close, this), EShortcut::GLOBAL_ACCEPT);
  140. if(queryID.getNum() > 0)
  141. quit->addCallback([=](){ LOCPLINT->cb->selectionMade(0, queryID); });
  142. questlogButton[0] = std::make_shared<CButton>(Point( 10, qeLayout ? 39 : 44), AnimationPath::builtin("hsbtns4.def"), CButton::tooltip(CGI->generaltexth->heroscrn[0]), std::bind(&CExchangeWindow::questLogShortcut, this), EShortcut::ADVENTURE_QUEST_LOG);
  143. questlogButton[1] = std::make_shared<CButton>(Point(740, qeLayout ? 39 : 44), AnimationPath::builtin("hsbtns4.def"), CButton::tooltip(CGI->generaltexth->heroscrn[0]), std::bind(&CExchangeWindow::questLogShortcut, this), EShortcut::ADVENTURE_QUEST_LOG);
  144. Rect barRect(5, 578, 725, 18);
  145. statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), barRect, 5, 578));
  146. //garrison interface
  147. garr = std::make_shared<CGarrisonInt>(Point(69, qeLayout ? 122 : 131), 4, Point(418,0), heroInst[0], heroInst[1], true, true);
  148. auto splitButtonCallback = [&](){ garr->splitClick(); };
  149. garr->addSplitBtn(std::make_shared<CButton>( Point( 10, qeLayout ? 122 : 132), AnimationPath::builtin("TSBTNS.DEF"), CButton::tooltip(CGI->generaltexth->tcommands[3]), splitButtonCallback, EShortcut::HERO_ARMY_SPLIT));
  150. garr->addSplitBtn(std::make_shared<CButton>( Point(744, qeLayout ? 122 : 132), AnimationPath::builtin("TSBTNS.DEF"), CButton::tooltip(CGI->generaltexth->tcommands[3]), splitButtonCallback, EShortcut::HERO_ARMY_SPLIT));
  151. if(qeLayout)
  152. {
  153. buttonMoveUnitsFromLeftToRight = std::make_shared<CButton>(Point(325, 118), AnimationPath::builtin("quick-exchange/armRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), [this](){ this->moveUnitsShortcut(true); });
  154. buttonMoveUnitsFromRightToLeft = std::make_shared<CButton>(Point(425, 118), AnimationPath::builtin("quick-exchange/armLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), [this](){ this->moveUnitsShortcut(false); });
  155. buttonMoveArtifactsFromLeftToRight = std::make_shared<CButton>(Point(325, 154), AnimationPath::builtin("quick-exchange/artRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), [this](){ this->moveArtifactsCallback(true);});
  156. buttonMoveArtifactsFromRightToLeft = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin("quick-exchange/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), [this](){ this->moveArtifactsCallback(false);});
  157. exchangeUnitsButton = std::make_shared<CButton>(Point(377, 118), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[2]), [this](){ controller.swapArmy(); });
  158. exchangeArtifactsButton = std::make_shared<CButton>(Point(377, 154), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[4]), [this](){ this->swapArtifactsCallback(); });
  159. backpackButtonLeft = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
  160. [this](){ this->backpackShortcut(true); });
  161. backpackButtonRight = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
  162. [this](){ this->backpackShortcut(false); });
  163. backpackButtonLeft->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
  164. backpackButtonRight->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
  165. auto leftHeroBlock = heroInst[0]->tempOwner != LOCPLINT->cb->getPlayerID();
  166. auto rightHeroBlock = heroInst[1]->tempOwner != LOCPLINT->cb->getPlayerID();
  167. buttonMoveUnitsFromLeftToRight->block(leftHeroBlock);
  168. buttonMoveUnitsFromRightToLeft->block(rightHeroBlock);
  169. buttonMoveArtifactsFromLeftToRight->block(leftHeroBlock);
  170. buttonMoveArtifactsFromRightToLeft->block(rightHeroBlock);
  171. exchangeUnitsButton->block(leftHeroBlock || rightHeroBlock);
  172. exchangeArtifactsButton->block(leftHeroBlock || rightHeroBlock);
  173. backpackButtonLeft->block(leftHeroBlock);
  174. backpackButtonRight->block(rightHeroBlock);
  175. for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
  176. {
  177. moveUnitFromRightToLeftButtons.push_back(
  178. std::make_shared<CButton>(
  179. Point(484 + 35 * i, 154),
  180. AnimationPath::builtin("quick-exchange/unitLeft.DEF"),
  181. CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
  182. std::bind(&CExchangeController::moveStack, &controller, false, SlotID(i))));
  183. moveUnitFromRightToLeftButtons.back()->block(leftHeroBlock);
  184. moveUnitFromLeftToRightButtons.push_back(
  185. std::make_shared<CButton>(
  186. Point(66 + 35 * i, 154),
  187. AnimationPath::builtin("quick-exchange/unitRight.DEF"),
  188. CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
  189. std::bind(&CExchangeController::moveStack, &controller, true, SlotID(i))));
  190. moveUnitFromLeftToRightButtons.back()->block(rightHeroBlock);
  191. }
  192. }
  193. updateWidgets();
  194. }
  195. void CExchangeWindow::moveArtifactsCallback(bool leftToRight)
  196. {
  197. bool moveEquipped = !GH.isKeyboardShiftDown();
  198. bool moveBackpack = !GH.isKeyboardCmdDown();
  199. controller.moveArtifacts(leftToRight, moveEquipped, moveBackpack);
  200. };
  201. void CExchangeWindow::swapArtifactsCallback()
  202. {
  203. bool moveEquipped = !GH.isKeyboardShiftDown();
  204. bool moveBackpack = !GH.isKeyboardCmdDown();
  205. controller.swapArtifacts(moveEquipped, moveBackpack);
  206. }
  207. void CExchangeWindow::moveUnitsShortcut(bool leftToRight)
  208. {
  209. std::optional<SlotID> slotId = std::nullopt;
  210. if(const auto * slot = getSelectedSlotID())
  211. slotId = slot->getSlot();
  212. controller.moveArmy(leftToRight, slotId);
  213. };
  214. void CExchangeWindow::backpackShortcut(bool leftHero)
  215. {
  216. GH.windows().createAndPushWindow<CHeroBackpackWindow>(heroInst[leftHero ? 0 : 1], artSets);
  217. };
  218. void CExchangeWindow::keyPressed(EShortcut key)
  219. {
  220. switch (key)
  221. {
  222. case EShortcut::EXCHANGE_ARMY_TO_LEFT:
  223. moveUnitsShortcut(false);
  224. break;
  225. case EShortcut::EXCHANGE_ARMY_TO_RIGHT:
  226. moveUnitsShortcut(true);
  227. break;
  228. case EShortcut::EXCHANGE_ARMY_SWAP:
  229. controller.swapArmy();
  230. break;
  231. case EShortcut::EXCHANGE_ARTIFACTS_TO_LEFT:
  232. controller.moveArtifacts(false, true, true);
  233. break;
  234. case EShortcut::EXCHANGE_ARTIFACTS_TO_RIGHT:
  235. controller.moveArtifacts(true, true, true);
  236. break;
  237. case EShortcut::EXCHANGE_ARTIFACTS_SWAP:
  238. controller.swapArtifacts(true, true);
  239. break;
  240. case EShortcut::EXCHANGE_EQUIPPED_TO_LEFT:
  241. controller.moveArtifacts(false, true, false);
  242. break;
  243. case EShortcut::EXCHANGE_EQUIPPED_TO_RIGHT:
  244. controller.moveArtifacts(true, true, false);
  245. break;
  246. case EShortcut::EXCHANGE_EQUIPPED_SWAP:
  247. controller.swapArtifacts(true, false);
  248. break;
  249. case EShortcut::EXCHANGE_BACKPACK_TO_LEFT:
  250. controller.moveArtifacts(false, false, true);
  251. break;
  252. case EShortcut::EXCHANGE_BACKPACK_TO_RIGHT:
  253. controller.moveArtifacts(true, false, true);
  254. break;
  255. case EShortcut::EXCHANGE_BACKPACK_SWAP:
  256. controller.swapArtifacts(false, true);
  257. break;
  258. case EShortcut::EXCHANGE_BACKPACK_LEFT:
  259. backpackShortcut(true);
  260. break;
  261. case EShortcut::EXCHANGE_BACKPACK_RIGHT:
  262. backpackShortcut(false);
  263. break;
  264. }
  265. }
  266. const CGarrisonSlot * CExchangeWindow::getSelectedSlotID() const
  267. {
  268. return garr->getSelection();
  269. }
  270. void CExchangeWindow::updateGarrisons()
  271. {
  272. garr->recreateSlots();
  273. updateWidgets();
  274. }
  275. bool CExchangeWindow::holdsGarrison(const CArmedInstance * army)
  276. {
  277. return garr->upperArmy() == army || garr->lowerArmy() == army;
  278. }
  279. void CExchangeWindow::questLogShortcut()
  280. {
  281. CCS->curh->dragAndDropCursor(nullptr);
  282. LOCPLINT->showQuestLog();
  283. }
  284. void CExchangeWindow::updateWidgets()
  285. {
  286. for(size_t leftRight : {0, 1})
  287. {
  288. const CGHeroInstance * hero = heroInst.at(leftRight);
  289. for(int m=0; m<GameConstants::PRIMARY_SKILLS; ++m)
  290. {
  291. auto value = heroInst[leftRight]->getPrimSkillLevel(static_cast<PrimarySkill>(m));
  292. primSkillValues[leftRight][m]->setText(std::to_string(value));
  293. }
  294. for(int m=0; m < hero->secSkills.size(); ++m)
  295. {
  296. int id = hero->secSkills[m].first;
  297. int level = hero->secSkills[m].second;
  298. secSkillIcons[leftRight][m]->setFrame(2 + id * 3 + level);
  299. }
  300. expValues[leftRight]->setText(TextOperations::formatMetric(hero->exp, 3));
  301. manaValues[leftRight]->setText(TextOperations::formatMetric(hero->mana, 3));
  302. morale[leftRight]->set(hero);
  303. luck[leftRight]->set(hero);
  304. }
  305. }