TradePanels.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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 "../../gui/CGuiHandler.h"
  13. #include "../../render/Canvas.h"
  14. #include "../../widgets/TextControls.h"
  15. #include "../../windows/InfoWindows.h"
  16. #include "../../CGameInfo.h"
  17. #include "../../CPlayerInterface.h"
  18. #include "../../../CCallback.h"
  19. #include "../../../lib/CGeneralTextHandler.h"
  20. #include "../../../lib/mapObjects/CGHeroInstance.h"
  21. CTradeableItem::CTradeableItem(const Rect & area, EType Type, int ID, bool Left, int Serial)
  22. : SelectableSlot(area, Point(1, 1))
  23. , artInstance(nullptr)
  24. , type(EType(-1)) // set to invalid, will be corrected in setType
  25. , id(ID)
  26. , serial(Serial)
  27. , left(Left)
  28. , downSelection(false)
  29. {
  30. OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
  31. addUsedEvents(LCLICK);
  32. addUsedEvents(HOVER);
  33. addUsedEvents(SHOW_POPUP);
  34. setType(Type);
  35. this->pos.w = area.w;
  36. this->pos.h = area.h;
  37. }
  38. void CTradeableItem::setType(EType newType)
  39. {
  40. if(type != newType)
  41. {
  42. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  43. type = newType;
  44. if(getIndex() < 0)
  45. {
  46. image = std::make_shared<CAnimImage>(getFilename(), 0);
  47. image->disable();
  48. }
  49. else
  50. {
  51. image = std::make_shared<CAnimImage>(getFilename(), getIndex());
  52. }
  53. }
  54. }
  55. void CTradeableItem::setID(int newID)
  56. {
  57. if(id != newID)
  58. {
  59. id = newID;
  60. if(image)
  61. {
  62. int index = getIndex();
  63. if(index < 0)
  64. image->disable();
  65. else
  66. {
  67. image->enable();
  68. image->setFrame(index);
  69. }
  70. }
  71. }
  72. }
  73. AnimationPath CTradeableItem::getFilename()
  74. {
  75. switch(type)
  76. {
  77. case EType::RESOURCE:
  78. return AnimationPath::builtin("RESOURCE");
  79. case EType::PLAYER:
  80. return AnimationPath::builtin("CREST58");
  81. case EType::ARTIFACT_TYPE:
  82. case EType::ARTIFACT_PLACEHOLDER:
  83. case EType::ARTIFACT_INSTANCE:
  84. return AnimationPath::builtin("artifact");
  85. case EType::CREATURE:
  86. return AnimationPath::builtin("TWCRPORT");
  87. default:
  88. return {};
  89. }
  90. }
  91. int CTradeableItem::getIndex()
  92. {
  93. if(id < 0)
  94. return -1;
  95. switch(type)
  96. {
  97. case EType::RESOURCE:
  98. case EType::PLAYER:
  99. return id;
  100. case EType::ARTIFACT_TYPE:
  101. case EType::ARTIFACT_INSTANCE:
  102. case EType::ARTIFACT_PLACEHOLDER:
  103. return CGI->artifacts()->getByIndex(id)->getIconIndex();
  104. case EType::CREATURE:
  105. return CGI->creatures()->getByIndex(id)->getIconIndex();
  106. default:
  107. return -1;
  108. }
  109. }
  110. void CTradeableItem::showAll(Canvas & to)
  111. {
  112. Point posToBitmap;
  113. Point posToSubCenter;
  114. switch(type)
  115. {
  116. case EType::RESOURCE:
  117. posToBitmap = Point(19, 9);
  118. posToSubCenter = Point(35, 57);
  119. break;
  120. case EType::CREATURE_PLACEHOLDER:
  121. case EType::CREATURE:
  122. posToSubCenter = Point(29, 77);
  123. break;
  124. case EType::PLAYER:
  125. posToSubCenter = Point(31, 77);
  126. break;
  127. case EType::ARTIFACT_PLACEHOLDER:
  128. case EType::ARTIFACT_INSTANCE:
  129. posToSubCenter = Point(22, 51);
  130. if (downSelection)
  131. posToSubCenter.y += 8;
  132. break;
  133. case EType::ARTIFACT_TYPE:
  134. posToSubCenter = Point(35, 57);
  135. posToBitmap = Point(13, 0);
  136. break;
  137. }
  138. if(image)
  139. {
  140. image->moveTo(pos.topLeft() + posToBitmap);
  141. CIntObject::showAll(to);
  142. }
  143. to.drawText(pos.topLeft() + posToSubCenter, FONT_SMALL, Colors::WHITE, ETextAlignment::CENTER, subtitle);
  144. }
  145. void CTradeableItem::clickPressed(const Point & cursorPosition)
  146. {
  147. if(clickPressedCallback)
  148. clickPressedCallback(shared_from_this());
  149. }
  150. void CTradeableItem::showAllAt(const Point & dstPos, const std::string & customSub, Canvas & to)
  151. {
  152. Rect oldPos = pos;
  153. std::string oldSub = subtitle;
  154. downSelection = true;
  155. moveTo(dstPos);
  156. subtitle = customSub;
  157. showAll(to);
  158. downSelection = false;
  159. moveTo(oldPos.topLeft());
  160. subtitle = oldSub;
  161. }
  162. void CTradeableItem::hover(bool on)
  163. {
  164. if(!on)
  165. {
  166. GH.statusbar()->clear();
  167. return;
  168. }
  169. switch(type)
  170. {
  171. case EType::CREATURE:
  172. case EType::CREATURE_PLACEHOLDER:
  173. GH.statusbar()->write(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->objects[id]->getNamePluralTranslated()));
  174. break;
  175. case EType::ARTIFACT_PLACEHOLDER:
  176. if(id < 0)
  177. GH.statusbar()->write(CGI->generaltexth->zelp[582].first);
  178. else
  179. GH.statusbar()->write(CGI->artifacts()->getByIndex(id)->getNameTranslated());
  180. break;
  181. }
  182. }
  183. void CTradeableItem::showPopupWindow(const Point & cursorPosition)
  184. {
  185. switch(type)
  186. {
  187. case EType::CREATURE:
  188. case EType::CREATURE_PLACEHOLDER:
  189. break;
  190. case EType::ARTIFACT_TYPE:
  191. case EType::ARTIFACT_PLACEHOLDER:
  192. //TODO: it's would be better for market to contain actual CArtifactInstance and not just ids of certain artifact type so we can use getEffectiveDescription.
  193. if (id >= 0)
  194. CRClickPopup::createAndPush(CGI->artifacts()->getByIndex(id)->getDescriptionTranslated());
  195. break;
  196. }
  197. }
  198. std::string CTradeableItem::getName(int number) const
  199. {
  200. switch(type)
  201. {
  202. case EType::PLAYER:
  203. return CGI->generaltexth->capColors[id];
  204. case EType::RESOURCE:
  205. return CGI->generaltexth->restypes[id];
  206. case EType::CREATURE:
  207. if (number == 1)
  208. return CGI->creh->objects[id]->getNameSingularTranslated();
  209. else
  210. return CGI->creh->objects[id]->getNamePluralTranslated();
  211. case EType::ARTIFACT_TYPE:
  212. case EType::ARTIFACT_INSTANCE:
  213. return CGI->artifacts()->getByIndex(id)->getNameTranslated();
  214. }
  215. logGlobal->error("Invalid trade item type: %d", (int)type);
  216. return "";
  217. }
  218. const CArtifactInstance * CTradeableItem::getArtInstance() const
  219. {
  220. switch(type)
  221. {
  222. case EType::ARTIFACT_PLACEHOLDER:
  223. case EType::ARTIFACT_INSTANCE:
  224. return artInstance;
  225. default:
  226. return nullptr;
  227. }
  228. }
  229. void CTradeableItem::setArtInstance(const CArtifactInstance * art)
  230. {
  231. assert(type == EType::ARTIFACT_PLACEHOLDER || type == EType::ARTIFACT_INSTANCE);
  232. artInstance = art;
  233. if(art)
  234. setID(art->getTypeId());
  235. else
  236. setID(-1);
  237. }
  238. void TradePanelBase::updateSlots()
  239. {
  240. if(updateSlotsCallback)
  241. updateSlotsCallback();
  242. }
  243. void TradePanelBase::deselect()
  244. {
  245. for(const auto & slot : slots)
  246. slot->selectSlot(false);
  247. }
  248. void TradePanelBase::clearSubtitles()
  249. {
  250. for(const auto & slot : slots)
  251. slot->subtitle.clear();
  252. }
  253. void TradePanelBase::updateOffer(CTradeableItem & slot, int cost, int qty)
  254. {
  255. slot.subtitle = std::to_string(qty);
  256. if(cost != 1)
  257. {
  258. slot.subtitle.append("/");
  259. slot.subtitle.append(std::to_string(cost));
  260. }
  261. }
  262. void TradePanelBase::deleteSlots()
  263. {
  264. if(deleteSlotsCheck)
  265. slots.erase(std::remove_if(slots.begin(), slots.end(), deleteSlotsCheck), slots.end());
  266. }
  267. void TradePanelBase::setSelectedFrameIndex(std::optional<size_t> index)
  268. {
  269. if(index.has_value())
  270. {
  271. selectedImage->enable();
  272. selectedImage->setFrame(index.value());
  273. }
  274. else
  275. {
  276. selectedImage->disable();
  277. selectedImage->setFrame(0);
  278. }
  279. }
  280. ResourcesPanel::ResourcesPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback, UpdateSlotsFunctor updateSubtitles)
  281. {
  282. assert(resourcesForTrade.size() == slotsPos.size());
  283. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  284. for(const auto & res : resourcesForTrade)
  285. {
  286. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(slotsPos[res.num], slotDimension),
  287. EType::RESOURCE, res.num, true, res.num));
  288. slot->clickPressedCallback = clickPressedCallback;
  289. slot->setSelectionWidth(selectionWidth);
  290. }
  291. updateSlotsCallback = updateSubtitles;
  292. selectedImage = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), 0);
  293. }
  294. ArtifactsPanel::ArtifactsPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback, UpdateSlotsFunctor updateSubtitles,
  295. const std::vector<TradeItemBuy> & arts)
  296. {
  297. assert(slotsForTrade == slotsPos.size());
  298. assert(slotsForTrade == arts.size());
  299. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  300. for(auto slotIdx = 0; slotIdx < slotsForTrade; slotIdx++)
  301. {
  302. auto artType = arts[slotIdx].getNum();
  303. if(artType != ArtifactID::NONE)
  304. {
  305. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(slotsPos[slotIdx], slotDimension),
  306. EType::ARTIFACT_TYPE, artType, false, slotIdx));
  307. slot->clickPressedCallback = clickPressedCallback;
  308. slot->setSelectionWidth(selectionWidth);
  309. }
  310. }
  311. updateSlotsCallback = updateSubtitles;
  312. }
  313. PlayersPanel::PlayersPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback)
  314. {
  315. assert(PlayerColor::PLAYER_LIMIT_I <= slotsPos.size() + 1);
  316. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  317. std::vector<PlayerColor> players;
  318. for(auto player = PlayerColor(0); player < PlayerColor::PLAYER_LIMIT_I; player++)
  319. {
  320. if(player != LOCPLINT->playerID && LOCPLINT->cb->getPlayerStatus(player) == EPlayerStatus::INGAME)
  321. players.emplace_back(player);
  322. }
  323. slots.resize(players.size());
  324. int slotNum = 0;
  325. for(auto & slot : slots)
  326. {
  327. slot = std::make_shared<CTradeableItem>(Rect(slotsPos[slotNum], slotDimension), EType::PLAYER, players[slotNum].num, false, slotNum);
  328. slot->clickPressedCallback = clickPressedCallback;
  329. slot->setSelectionWidth(selectionWidth);
  330. slot->subtitle = CGI->generaltexth->capColors[players[slotNum].num];
  331. slotNum++;
  332. }
  333. }
  334. CreaturesPanel::CreaturesPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback, const slotsData & initialSlots)
  335. {
  336. assert(initialSlots.size() <= GameConstants::ARMY_SIZE);
  337. assert(slotsPos.size() <= GameConstants::ARMY_SIZE);
  338. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  339. for(const auto & [creatureId, slotId, creaturesNum] : initialSlots)
  340. {
  341. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(slotsPos[slotId.num], slotDimension),
  342. creaturesNum == 0 ? EType::CREATURE_PLACEHOLDER : EType::CREATURE, creatureId.num, true, slotId));
  343. slot->clickPressedCallback = clickPressedCallback;
  344. if(creaturesNum != 0)
  345. slot->subtitle = std::to_string(creaturesNum);
  346. slot->setSelectionWidth(selectionWidth);
  347. }
  348. selectedImage = std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), 0);
  349. }
  350. CreaturesPanel::CreaturesPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback,
  351. const std::vector<std::shared_ptr<CTradeableItem>> & srcSlots, bool emptySlots)
  352. {
  353. assert(slots.size() <= GameConstants::ARMY_SIZE);
  354. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  355. for(const auto & srcSlot : srcSlots)
  356. {
  357. auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(slotsPos[srcSlot->serial], srcSlot->pos.dimensions()),
  358. emptySlots ? EType::CREATURE_PLACEHOLDER : EType::CREATURE, srcSlot->id, true, srcSlot->serial));
  359. slot->clickPressedCallback = clickPressedCallback;
  360. slot->subtitle = emptySlots ? "" : srcSlot->subtitle;
  361. slot->setSelectionWidth(selectionWidth);
  362. }
  363. selectedImage = std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), 0);
  364. }