TradePanels.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /*
  2. * TradePanels.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 "TradePanels.h"
  12. #include "../../GameEngine.h"
  13. #include "../../GameInstance.h"
  14. #include "../../render/Canvas.h"
  15. #include "../../widgets/Slider.h"
  16. #include "../../widgets/TextControls.h"
  17. #include "../../windows/InfoWindows.h"
  18. #include "../../CPlayerInterface.h"
  19. #include "../../../lib/GameLibrary.h"
  20. #include "../../../lib/callback/CCallback.h"
  21. #include "../../../lib/entities/artifact/CArtHandler.h"
  22. #include "../../../lib/texts/CGeneralTextHandler.h"
  23. #include "../../../lib/mapObjects/CGHeroInstance.h"
  24. CTradeableItem::CTradeableItem(const Rect & area, EType Type, int32_t ID, int32_t serial)
  25. : SelectableSlot(area, Point(1, 1))
  26. , type(EType(-1)) // set to invalid, will be corrected in setType
  27. , id(ID)
  28. , serial(serial)
  29. {
  30. OBJECT_CONSTRUCTION;
  31. addUsedEvents(LCLICK);
  32. addUsedEvents(HOVER);
  33. addUsedEvents(SHOW_POPUP);
  34. subtitle = std::make_shared<CLabel>(0, 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
  35. setType(Type);
  36. this->pos.w = area.w;
  37. this->pos.h = area.h;
  38. }
  39. void CTradeableItem::setType(EType newType)
  40. {
  41. if(type != newType)
  42. {
  43. OBJECT_CONSTRUCTION;
  44. type = newType;
  45. if(getIndex() < 0)
  46. {
  47. image = std::make_shared<CAnimImage>(getFilename(), 0);
  48. image->disable();
  49. }
  50. else
  51. {
  52. image = std::make_shared<CAnimImage>(getFilename(), getIndex());
  53. }
  54. switch(type)
  55. {
  56. case EType::RESOURCE:
  57. subtitle->moveTo(pos.topLeft() + Point(35, 55));
  58. image->moveTo(pos.topLeft() + Point(19, 8));
  59. break;
  60. case EType::CREATURE:
  61. subtitle->moveTo(pos.topLeft() + Point(30, 77));
  62. break;
  63. case EType::PLAYER:
  64. subtitle->moveTo(pos.topLeft() + Point(31, 76));
  65. break;
  66. case EType::ARTIFACT:
  67. subtitle->moveTo(pos.topLeft() + Point(21, 55));
  68. break;
  69. case EType::ARTIFACT_TYPE:
  70. subtitle->moveTo(pos.topLeft() + Point(35, 57));
  71. image->moveTo(pos.topLeft() + Point(13, 0));
  72. break;
  73. }
  74. }
  75. }
  76. void CTradeableItem::setID(int32_t newID)
  77. {
  78. if(id != newID)
  79. {
  80. id = newID;
  81. if(image)
  82. {
  83. const auto index = getIndex();
  84. if(index < 0)
  85. image->disable();
  86. else
  87. {
  88. image->enable();
  89. image->setFrame(index);
  90. }
  91. }
  92. }
  93. }
  94. void CTradeableItem::clear()
  95. {
  96. setID(-1);
  97. image->setFrame(0);
  98. image->disable();
  99. subtitle->clear();
  100. }
  101. AnimationPath CTradeableItem::getFilename()
  102. {
  103. switch(type)
  104. {
  105. case EType::RESOURCE:
  106. return AnimationPath::builtin("RESOURCE");
  107. case EType::PLAYER:
  108. return AnimationPath::builtin("CREST58");
  109. case EType::ARTIFACT_TYPE:
  110. case EType::ARTIFACT:
  111. return AnimationPath::builtin("artifact");
  112. case EType::CREATURE:
  113. return AnimationPath::builtin("TWCRPORT");
  114. default:
  115. return {};
  116. }
  117. }
  118. int CTradeableItem::getIndex()
  119. {
  120. if(id < 0)
  121. return -1;
  122. switch(type)
  123. {
  124. case EType::RESOURCE:
  125. case EType::PLAYER:
  126. return id;
  127. case EType::ARTIFACT_TYPE:
  128. case EType::ARTIFACT:
  129. return LIBRARY->artifacts()->getByIndex(id)->getIconIndex();
  130. case EType::CREATURE:
  131. return LIBRARY->creatures()->getByIndex(id)->getIconIndex();
  132. default:
  133. return -1;
  134. }
  135. }
  136. void CTradeableItem::clickPressed(const Point & cursorPosition)
  137. {
  138. if(clickPressedCallback)
  139. clickPressedCallback(shared_from_this());
  140. }
  141. void CTradeableItem::hover(bool on)
  142. {
  143. if(!on || id == -1)
  144. {
  145. ENGINE->statusbar()->clear();
  146. return;
  147. }
  148. switch(type)
  149. {
  150. case EType::CREATURE:
  151. ENGINE->statusbar()->write(boost::str(boost::format(LIBRARY->generaltexth->allTexts[481]) % LIBRARY->creh->objects[id]->getNamePluralTranslated()));
  152. break;
  153. case EType::ARTIFACT_TYPE:
  154. case EType::ARTIFACT:
  155. if(id < 0)
  156. ENGINE->statusbar()->write(LIBRARY->generaltexth->zelp[582].first);
  157. else
  158. ENGINE->statusbar()->write(LIBRARY->artifacts()->getByIndex(id)->getNameTranslated());
  159. break;
  160. case EType::RESOURCE:
  161. ENGINE->statusbar()->write(LIBRARY->generaltexth->restypes[id]);
  162. break;
  163. case EType::PLAYER:
  164. ENGINE->statusbar()->write(LIBRARY->generaltexth->capColors[id]);
  165. break;
  166. }
  167. }
  168. void CTradeableItem::showPopupWindow(const Point & cursorPosition)
  169. {
  170. switch(type)
  171. {
  172. case EType::CREATURE:
  173. break;
  174. case EType::ARTIFACT_TYPE:
  175. case EType::ARTIFACT:
  176. if (id >= 0)
  177. CRClickPopup::createAndPush(LIBRARY->artifacts()->getByIndex(id)->getDescriptionTranslated());
  178. break;
  179. }
  180. }
  181. void TradePanelBase::update()
  182. {
  183. if(deleteSlotsCheck)
  184. slots.erase(std::remove_if(slots.begin(), slots.end(), deleteSlotsCheck), slots.end());
  185. if(updateSlotsCallback)
  186. updateSlotsCallback();
  187. }
  188. void TradePanelBase::deselect()
  189. {
  190. for(const auto & slot : slots)
  191. slot->selectSlot(false);
  192. }
  193. void TradePanelBase::clearSubtitles()
  194. {
  195. for(const auto & slot : slots)
  196. slot->subtitle->clear();
  197. }
  198. void TradePanelBase::updateOffer(CTradeableItem & slot, int cost, int qty)
  199. {
  200. std::string subtitle = std::to_string(qty);
  201. if(cost != 1)
  202. {
  203. subtitle.append("/");
  204. subtitle.append(std::to_string(cost));
  205. }
  206. slot.subtitle->setText(subtitle);
  207. }
  208. void TradePanelBase::setShowcaseSubtitle(const std::string & text)
  209. {
  210. showcaseSlot->subtitle->setText(text);
  211. }
  212. int32_t TradePanelBase::getHighlightedItemId() const
  213. {
  214. if(highlightedSlot)
  215. return highlightedSlot->id;
  216. else
  217. return -1;
  218. }
  219. void TradePanelBase::onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot)
  220. {
  221. assert(vstd::contains(slots, newSlot));
  222. if(newSlot == highlightedSlot)
  223. return;
  224. if(highlightedSlot)
  225. highlightedSlot->selectSlot(false);
  226. highlightedSlot = newSlot;
  227. newSlot->selectSlot(true);
  228. }
  229. bool TradePanelBase::isHighlighted() const
  230. {
  231. return highlightedSlot != nullptr;
  232. }
  233. ResourcesPanel::ResourcesPanel(const CTradeableItem::ClickPressedFunctor & clickPressedCallback,
  234. const UpdateSlotsFunctor & updateSubtitles)
  235. : clickPressedCallback(std::move(clickPressedCallback))
  236. {
  237. OBJECT_CONSTRUCTION;
  238. // TODO: replace with LIBRARY->resourceTypeHandler->getAllObjects() -> also check order after doing this
  239. resourcesForTrade =
  240. {
  241. GameResID::WOOD, GameResID::MERCURY, GameResID::ORE,
  242. GameResID::SULFUR, GameResID::CRYSTAL, GameResID::GEMS,
  243. GameResID::GOLD,
  244. GameResID::GOLD, // TODO: remove
  245. GameResID::GOLD, // TODO: remove
  246. GameResID::GOLD // TODO: remove
  247. };
  248. int lines = vstd::divideAndCeil(resourcesForTrade.size(), 3);
  249. if(lines > 3)
  250. {
  251. slider = std::make_shared<CSlider>(Point(240, 0), 224, [this](int to){ updateSlots(to); setRedrawParent(true); redraw(); }, 3, lines, 0, Orientation::VERTICAL, CSlider::BROWN);
  252. slider->setPanningStep(72);
  253. slider->setScrollBounds(Rect(-240, 0, 240, 224));
  254. }
  255. updateSlotsCallback = std::move(updateSubtitles);
  256. updateSlots(0);
  257. showcaseSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::RESOURCE, 0, 0);
  258. }
  259. void ResourcesPanel::updateSlots(int line)
  260. {
  261. OBJECT_CONSTRUCTION;
  262. clickPressedCallback(nullptr);
  263. int offset = line * 3;
  264. slots.clear();
  265. for (int i = 0; i < std::min(static_cast<int>(resourcesForTrade.size() - offset), 9); i++)
  266. {
  267. const auto& res = resourcesForTrade[i + offset];
  268. auto slotPos = slotsPos[i];
  269. if(resourcesForTrade.size() == 7 && i == 6) // for 7 ressources place gold in the middle
  270. slotPos = slotsPos[i + 1];
  271. auto slotPtr = std::make_shared<CTradeableItem>(Rect(slotPos, slotDimension), EType::RESOURCE, res.num, res.num);
  272. slotPtr->clickPressedCallback = clickPressedCallback;
  273. slotPtr->setSelectionWidth(selectionWidth);
  274. slots.push_back(slotPtr);
  275. }
  276. updateSlotsCallback();
  277. }
  278. ArtifactsPanel::ArtifactsPanel(const CTradeableItem::ClickPressedFunctor & clickPressedCallback,
  279. const UpdateSlotsFunctor & updateSubtitles, const std::vector<TradeItemBuy> & arts)
  280. {
  281. assert(slotsForTrade == slotsPos.size());
  282. assert(slotsForTrade == arts.size());
  283. OBJECT_CONSTRUCTION;
  284. for(auto slotIdx = 0; slotIdx < slotsForTrade; slotIdx++)
  285. {
  286. auto artType = arts[slotIdx].getNum();
  287. if(artType != ArtifactID::NONE)
  288. {
  289. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(slotsPos[slotIdx], slotDimension),
  290. EType::ARTIFACT_TYPE, artType, slotIdx));
  291. slot->clickPressedCallback = clickPressedCallback;
  292. slot->setSelectionWidth(selectionWidth);
  293. }
  294. }
  295. updateSlotsCallback = updateSubtitles;
  296. showcaseSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::ARTIFACT_TYPE, 0, 0);
  297. showcaseSlot->subtitle->moveBy(Point(0, 1));
  298. }
  299. PlayersPanel::PlayersPanel(const CTradeableItem::ClickPressedFunctor & clickPressedCallback)
  300. {
  301. assert(PlayerColor::PLAYER_LIMIT_I <= slotsPos.size() + 1);
  302. OBJECT_CONSTRUCTION;
  303. std::vector<PlayerColor> players;
  304. for(auto player = PlayerColor(0); player < PlayerColor::PLAYER_LIMIT_I; player++)
  305. {
  306. if(player != GAME->interface()->playerID && GAME->interface()->cb->getPlayerStatus(player) == EPlayerStatus::INGAME)
  307. players.emplace_back(player);
  308. }
  309. slots.resize(players.size());
  310. int slotNum = 0;
  311. for(auto & slot : slots)
  312. {
  313. slot = std::make_shared<CTradeableItem>(Rect(slotsPos[slotNum], slotDimension), EType::PLAYER, players[slotNum].num, slotNum);
  314. slot->clickPressedCallback = clickPressedCallback;
  315. slot->setSelectionWidth(selectionWidth);
  316. slot->subtitle->setText(LIBRARY->generaltexth->capColors[players[slotNum].num]);
  317. slotNum++;
  318. }
  319. showcaseSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::PLAYER, 0, 0);
  320. }
  321. CreaturesPanel::CreaturesPanel(const CTradeableItem::ClickPressedFunctor & clickPressedCallback, const slotsData & initialSlots)
  322. {
  323. assert(initialSlots.size() <= GameConstants::ARMY_SIZE);
  324. assert(slotsPos.size() <= GameConstants::ARMY_SIZE);
  325. OBJECT_CONSTRUCTION;
  326. for(const auto & [creatureId, slotId, creaturesNum] : initialSlots)
  327. {
  328. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(slotsPos[slotId.num], slotDimension),
  329. EType::CREATURE, creaturesNum == 0 ? -1 : creatureId.num, slotId));
  330. slot->clickPressedCallback = clickPressedCallback;
  331. if(creaturesNum != 0)
  332. slot->subtitle->setText(std::to_string(creaturesNum));
  333. slot->setSelectionWidth(selectionWidth);
  334. }
  335. showcaseSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::CREATURE, 0, 0);
  336. }
  337. CreaturesPanel::CreaturesPanel(const CTradeableItem::ClickPressedFunctor & clickPressedCallback,
  338. const std::vector<std::shared_ptr<CTradeableItem>> & srcSlots, bool emptySlots)
  339. {
  340. assert(slots.size() <= GameConstants::ARMY_SIZE);
  341. OBJECT_CONSTRUCTION;
  342. for(const auto & srcSlot : srcSlots)
  343. {
  344. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(slotsPos[srcSlot->serial], srcSlot->pos.dimensions()),
  345. EType::CREATURE, emptySlots ? -1 : srcSlot->id, srcSlot->serial));
  346. slot->clickPressedCallback = clickPressedCallback;
  347. slot->subtitle->setText(emptySlots ? "" : srcSlot->subtitle->getText());
  348. slot->setSelectionWidth(selectionWidth);
  349. }
  350. showcaseSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::CREATURE, 0, 0);
  351. }
  352. ArtifactsAltarPanel::ArtifactsAltarPanel(const CTradeableItem::ClickPressedFunctor & clickPressedCallback)
  353. {
  354. OBJECT_CONSTRUCTION;
  355. int slotNum = 0;
  356. for(auto & altarSlotPos : slotsPos)
  357. {
  358. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(altarSlotPos, Point(44, 44)), EType::ARTIFACT, -1, slotNum));
  359. slot->clickPressedCallback = clickPressedCallback;
  360. slot->subtitle->clear();
  361. slot->subtitle->moveBy(Point(0, -1));
  362. slotNum++;
  363. }
  364. showcaseSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::ARTIFACT_TYPE, 0, 0);
  365. showcaseSlot->subtitle->moveBy(Point(0, 3));
  366. }