CArtifactHolder.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. #include "StdInc.h"
  2. #include "CArtifactHolder.h"
  3. #include "../gui/CGuiHandler.h"
  4. #include "../gui/CCursorHandler.h"
  5. #include "Buttons.h"
  6. #include "CComponent.h"
  7. #include "../windows/CHeroWindow.h"
  8. #include "../windows/CSpellWindow.h"
  9. #include "../windows/GUIClasses.h"
  10. #include "../CPlayerInterface.h"
  11. #include "../CGameInfo.h"
  12. #include "../../CCallback.h"
  13. #include "../../lib/CArtHandler.h"
  14. #include "../../lib/spells/CSpellHandler.h"
  15. #include "../../lib/CGeneralTextHandler.h"
  16. #include "../../lib/mapObjects/CGHeroInstance.h"
  17. /*
  18. * CArtifactHolder.cpp, part of VCMI engine
  19. *
  20. * Authors: listed in file AUTHORS in main folder
  21. *
  22. * License: GNU General Public License v2.0 or later
  23. * Full text of license available in license.txt file, in main folder
  24. *
  25. */
  26. CHeroArtPlace::CHeroArtPlace(Point position, const CArtifactInstance * Art): CArtPlace(position, Art),
  27. locked(false), picked(false), marked(false), ourOwner(nullptr)
  28. {
  29. createImage();
  30. }
  31. void CHeroArtPlace::createImage()
  32. {
  33. OBJ_CONSTRUCTION_CAPTURING_ALL;
  34. int imageIndex = 0;
  35. if (ourArt)
  36. imageIndex = ourArt->artType->iconIndex;
  37. if (locked)
  38. imageIndex = ArtifactID::ART_LOCK;
  39. image = new CAnimImage("artifact", imageIndex);
  40. if (!ourArt)
  41. image->disable();
  42. selection = new CAnimImage("artifact", ArtifactID::ART_SELECTION);
  43. selection->disable();
  44. }
  45. void CHeroArtPlace::lockSlot(bool on)
  46. {
  47. if (locked == on)
  48. return;
  49. locked = on;
  50. if (on)
  51. image->setFrame(ArtifactID::ART_LOCK);
  52. else
  53. image->setFrame(ourArt->artType->iconIndex);
  54. }
  55. void CHeroArtPlace::pickSlot(bool on)
  56. {
  57. if (picked == on)
  58. return;
  59. picked = on;
  60. if (on)
  61. image->disable();
  62. else
  63. image->enable();
  64. }
  65. void CHeroArtPlace::selectSlot(bool on)
  66. {
  67. if (marked == on)
  68. return;
  69. marked = on;
  70. if (on)
  71. selection->enable();
  72. else
  73. selection->disable();
  74. }
  75. void CHeroArtPlace::clickLeft(tribool down, bool previousState)
  76. {
  77. //LRClickableAreaWTextComp::clickLeft(down);
  78. bool inBackpack = slotID >= GameConstants::BACKPACK_START,
  79. srcInBackpack = ourOwner->commonInfo->src.slotID >= GameConstants::BACKPACK_START,
  80. srcInSameHero = ourOwner->commonInfo->src.AOH == ourOwner;
  81. if(ourOwner->highlightModeCallback && ourArt)
  82. {
  83. if(down)
  84. {
  85. if(!ourArt->artType->isTradable()) //War Machine or Spellbook
  86. {
  87. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); //This item can't be traded.
  88. }
  89. else
  90. {
  91. ourOwner->unmarkSlots(false);
  92. selectSlot(true);
  93. ourOwner->highlightModeCallback(this);
  94. }
  95. }
  96. return;
  97. }
  98. // If clicked on spellbook, open it only if no artifact is held at the moment.
  99. if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
  100. {
  101. if(ourArt->artType->id == ArtifactID::SPELLBOOK)
  102. GH.pushInt(new CSpellWindow(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt));
  103. }
  104. if (!down && previousState)
  105. {
  106. if(ourArt && ourArt->artType->id == ArtifactID::SPELLBOOK)
  107. return; //this is handled separately
  108. if(!ourOwner->commonInfo->src.AOH) //nothing has been clicked
  109. {
  110. if(ourArt //to prevent selecting empty slots (bugfix to what GrayFace reported)
  111. && ourOwner->curHero->tempOwner == LOCPLINT->playerID)//can't take art from another player
  112. {
  113. if(ourArt->artType->id == ArtifactID::CATAPULT) //catapult cannot be highlighted
  114. {
  115. std::vector<CComponent *> catapult(1, new CComponent(CComponent::artifact, 3, 0));
  116. LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); //The Catapult must be equipped.
  117. return;
  118. }
  119. select();
  120. }
  121. }
  122. else if(ourArt == ourOwner->commonInfo->src.art) //restore previously picked artifact
  123. {
  124. deselect();
  125. }
  126. else //perform artifact transition
  127. {
  128. if(inBackpack) // Backpack destination.
  129. {
  130. if(srcInBackpack && slotID == ourOwner->commonInfo->src.slotID + 1) //next slot (our is not visible, so visually same as "old" place) to the art -> make nothing, return artifact to slot
  131. {
  132. deselect();
  133. }
  134. else
  135. {
  136. const CArtifact * const cur = ourOwner->commonInfo->src.art->artType;
  137. switch(cur->id)
  138. {
  139. case ArtifactID::CATAPULT:
  140. //should not happen, catapult cannot be selected
  141. assert(cur->id != ArtifactID::CATAPULT);
  142. break;
  143. case ArtifactID::BALLISTA: case ArtifactID::AMMO_CART: case ArtifactID::FIRST_AID_TENT: //war machines cannot go to backpack
  144. LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->Name()));
  145. break;
  146. default:
  147. setMeAsDest();
  148. vstd::amin(ourOwner->commonInfo->dst.slotID, ArtifactPosition(
  149. ourOwner->curHero->artifactsInBackpack.size() + GameConstants::BACKPACK_START));
  150. if(srcInBackpack && srcInSameHero)
  151. {
  152. if(!ourArt //cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact
  153. || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
  154. vstd::advance(ourOwner->commonInfo->dst.slotID, -1);
  155. }
  156. if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst
  157. deselect();
  158. else
  159. ourOwner->realizeCurrentTransaction();
  160. break;
  161. }
  162. }
  163. }
  164. //check if swap is possible
  165. else if (fitsHere(ourOwner->commonInfo->src.art) &&
  166. (!ourArt || ourOwner->curHero->tempOwner == LOCPLINT->playerID))
  167. {
  168. setMeAsDest();
  169. //
  170. // // Special case when the dest artifact can't be fit into the src slot.
  171. // //CGI->arth->unequipArtifact(ourOwner->curHero->artifWorn, slotID);
  172. // const CArtifactsOfHero* srcAOH = ourOwner->commonInfo->src.AOH;
  173. // ui16 srcSlotID = ourOwner->commonInfo->src.slotID;
  174. // if (ourArt && srcSlotID < 19 && !ourArt->canBePutAt(ArtifactLocation(srcAOH->curHero, srcSlotID)))
  175. // {
  176. // // Put dest artifact into owner's backpack.
  177. // ourOwner->commonInfo->src.AOH = ourOwner;
  178. // ourOwner->commonInfo->src.slotID = ourOwner->curHero->artifacts.size() + 19;
  179. // }
  180. ourOwner->realizeCurrentTransaction();
  181. }
  182. }
  183. }
  184. }
  185. bool CHeroArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
  186. const CGHeroInstance *hero)
  187. {
  188. assert(art != nullptr);
  189. assert(hero != nullptr);
  190. std::vector<const CArtifact *> assemblyPossibilities = art->assemblyPossibilities(hero);
  191. // If the artifact can be assembled, display dialog.
  192. for(const CArtifact *combination : assemblyPossibilities)
  193. {
  194. LOCPLINT->showArtifactAssemblyDialog(
  195. art->artType->id,
  196. combination->id,
  197. true,
  198. std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id),
  199. 0);
  200. if(assemblyPossibilities.size() > 2)
  201. {
  202. logGlobal->warnStream() << boost::format(
  203. "More than one possibility of assembling on %s... taking only first")
  204. % art->artType->Name();
  205. }
  206. return true;
  207. }
  208. return false;
  209. }
  210. void CHeroArtPlace::clickRight(tribool down, bool previousState)
  211. {
  212. if(ourArt && down && ourArt && !locked && text.size() && !picked) //if there is no description or it's a lock, do nothing ;]
  213. {
  214. if (slotID < GameConstants::BACKPACK_START)
  215. {
  216. if(ourOwner->allowedAssembling)
  217. {
  218. std::vector<const CArtifact *> assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero);
  219. // If the artifact can be assembled, display dialog.
  220. if (askToAssemble(ourArt, slotID, ourOwner->curHero))
  221. {
  222. return;
  223. }
  224. // Otherwise if the artifact can be diasassembled, display dialog.
  225. if(ourArt->canBeDisassembled())
  226. {
  227. LOCPLINT->showArtifactAssemblyDialog(
  228. ourArt->artType->id,
  229. 0,
  230. false,
  231. std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, false, ArtifactID()),
  232. 0);
  233. return;
  234. }
  235. }
  236. }
  237. // Lastly just show the artifact description.
  238. LRClickableAreaWTextComp::clickRight(down, previousState);
  239. }
  240. }
  241. /**
  242. * Selects artifact slot so that the containing artifact looks like it's picked up.
  243. */
  244. void CHeroArtPlace::select ()
  245. {
  246. if (locked)
  247. return;
  248. selectSlot(true);
  249. pickSlot(true);
  250. if(ourArt->canBeDisassembled() && slotID < GameConstants::BACKPACK_START) //worn combined artifact -> locks have to disappear
  251. {
  252. for(int i = 0; i < GameConstants::BACKPACK_START; i++)
  253. {
  254. CHeroArtPlace * ap = ourOwner->getArtPlace(i);
  255. if(nullptr != ap)//getArtPlace may return null
  256. ap->pickSlot(ourArt->isPart(ap->ourArt));
  257. }
  258. }
  259. CCS->curh->dragAndDropCursor(new CAnimImage("artifact", ourArt->artType->iconIndex));
  260. ourOwner->commonInfo->src.setTo(this, false);
  261. ourOwner->markPossibleSlots(ourArt);
  262. if(slotID >= GameConstants::BACKPACK_START)
  263. ourOwner->scrollBackpack(0); //will update slots
  264. ourOwner->updateParentWindow();
  265. ourOwner->safeRedraw();
  266. }
  267. /**
  268. * Deselects the artifact slot.
  269. */
  270. void CHeroArtPlace::deselect ()
  271. {
  272. pickSlot(false);
  273. if(ourArt && ourArt->canBeDisassembled()) //combined art returned to its slot -> restore locks
  274. {
  275. for(int i = 0; i < GameConstants::BACKPACK_START; i++)
  276. {
  277. auto place = ourOwner->getArtPlace(i);
  278. if(nullptr != place)//getArtPlace may return null
  279. place->pickSlot(false);
  280. }
  281. }
  282. CCS->curh->dragAndDropCursor(nullptr);
  283. ourOwner->unmarkSlots();
  284. ourOwner->commonInfo->src.clear();
  285. if(slotID >= GameConstants::BACKPACK_START)
  286. ourOwner->scrollBackpack(0); //will update slots
  287. ourOwner->updateParentWindow();
  288. ourOwner->safeRedraw();
  289. }
  290. void CHeroArtPlace::showAll(SDL_Surface * to)
  291. {
  292. if (ourArt && !picked && ourArt == ourOwner->curHero->getArt(slotID, false)) //last condition is needed for disassembling -> artifact may be gone, but we don't know yet TODO: real, nice solution
  293. {
  294. CIntObject::showAll(to);
  295. }
  296. if(marked && active)
  297. {
  298. // Draw vertical bars.
  299. for (int i = 0; i < pos.h; ++i)
  300. {
  301. CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x, pos.y + i, 240, 220, 120);
  302. CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + pos.w - 1, pos.y + i, 240, 220, 120);
  303. }
  304. // Draw horizontal bars.
  305. for (int i = 0; i < pos.w; ++i)
  306. {
  307. CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + i, pos.y, 240, 220, 120);
  308. CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + i, pos.y + pos.h - 1, 240, 220, 120);
  309. }
  310. }
  311. }
  312. bool CHeroArtPlace::fitsHere(const CArtifactInstance * art) const
  313. {
  314. // You can place 'no artifact' anywhere.
  315. if(!art)
  316. return true;
  317. // Anything but War Machines can be placed in backpack.
  318. if (slotID >= GameConstants::BACKPACK_START)
  319. return !CGI->arth->isBigArtifact(art->artType->id);
  320. return art->canBePutAt(ArtifactLocation(ourOwner->curHero, slotID), true);
  321. }
  322. void CHeroArtPlace::setMeAsDest(bool backpackAsVoid /*= true*/)
  323. {
  324. ourOwner->commonInfo->dst.setTo(this, backpackAsVoid);
  325. }
  326. void CHeroArtPlace::setArtifact(const CArtifactInstance *art)
  327. {
  328. baseType = -1; //by default we don't store any component
  329. ourArt = art;
  330. if(!art)
  331. {
  332. image->disable();
  333. text = std::string();
  334. hoverText = CGI->generaltexth->allTexts[507];
  335. return;
  336. }
  337. image->enable();
  338. image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
  339. text = art->getEffectiveDescription(ourOwner->curHero);
  340. if(art->artType->id == ArtifactID::SPELL_SCROLL)
  341. {
  342. int spellID = art->getGivenSpellID();
  343. if(spellID >= 0)
  344. {
  345. //add spell component info (used to provide a pic in r-click popup)
  346. baseType = CComponent::spell;
  347. type = spellID;
  348. bonusValue = 0;
  349. }
  350. }
  351. else
  352. {
  353. baseType = CComponent::artifact;
  354. type = art->artType->id;
  355. bonusValue = 0;
  356. }
  357. if (locked) // Locks should appear as empty.
  358. hoverText = CGI->generaltexth->allTexts[507];
  359. else
  360. hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
  361. }
  362. void CArtifactsOfHero::SCommonPart::reset()
  363. {
  364. src.clear();
  365. dst.clear();
  366. CCS->curh->dragAndDropCursor(nullptr);
  367. }
  368. void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
  369. {
  370. curHero = hero;
  371. if (curHero->artifactsInBackpack.size() > 0)
  372. backpackPos %= curHero->artifactsInBackpack.size();
  373. else
  374. backpackPos = 0;
  375. // Fill the slots for worn artifacts and backpack.
  376. for(auto p : artWorn)
  377. {
  378. setSlotData(p.second, p.first);
  379. }
  380. scrollBackpack(0);
  381. }
  382. void CArtifactsOfHero::dispose()
  383. {
  384. CCS->curh->dragAndDropCursor(nullptr);
  385. }
  386. void CArtifactsOfHero::scrollBackpack(int dir)
  387. {
  388. int artsInBackpack = curHero->artifactsInBackpack.size();
  389. backpackPos += dir;
  390. if(backpackPos < 0)// No guarantee of modulus behavior with negative operands -> we keep it positive
  391. backpackPos += artsInBackpack;
  392. if(artsInBackpack)
  393. backpackPos %= artsInBackpack;
  394. std::multiset<const CArtifactInstance *> toOmit = artifactsOnAltar;
  395. if(commonInfo->src.art) //if we picked an art from backapck, its slot has to be omitted
  396. toOmit.insert(commonInfo->src.art);
  397. int omitedSoFar = 0;
  398. //set new data
  399. size_t s = 0;
  400. for( ; s < artsInBackpack; ++s)
  401. {
  402. if (s < artsInBackpack)
  403. {
  404. auto slotID = ArtifactPosition(GameConstants::BACKPACK_START + (s + backpackPos)%artsInBackpack);
  405. const CArtifactInstance *art = curHero->getArt(slotID);
  406. assert(art);
  407. if(!vstd::contains(toOmit, art))
  408. {
  409. if(s - omitedSoFar < backpack.size())
  410. setSlotData(backpack[s-omitedSoFar], slotID);
  411. }
  412. else
  413. {
  414. toOmit -= art;
  415. omitedSoFar++;
  416. continue;
  417. }
  418. }
  419. }
  420. for( ; s - omitedSoFar < backpack.size(); s++)
  421. eraseSlotData(backpack[s-omitedSoFar], ArtifactPosition(GameConstants::BACKPACK_START + s));
  422. //in artifact merchant selling artifacts we may have highlight on one of backpack artifacts -> market needs update, cause artifact under highlight changed
  423. if(highlightModeCallback)
  424. {
  425. for(auto & elem : backpack)
  426. {
  427. if(elem->marked)
  428. {
  429. highlightModeCallback(elem);
  430. break;
  431. }
  432. }
  433. }
  434. //blocking scrolling if there is not enough artifacts to scroll
  435. bool scrollingPossible = artsInBackpack - omitedSoFar > backpack.size();
  436. leftArtRoll->block(!scrollingPossible);
  437. rightArtRoll->block(!scrollingPossible);
  438. safeRedraw();
  439. }
  440. /**
  441. * Marks possible slots where a given artifact can be placed, except backpack.
  442. *
  443. * @param art Artifact checked against.
  444. */
  445. void CArtifactsOfHero::markPossibleSlots(const CArtifactInstance* art)
  446. {
  447. for(CArtifactsOfHero *aoh : commonInfo->participants)
  448. for(auto p : aoh->artWorn)
  449. p.second->selectSlot(art->canBePutAt(ArtifactLocation(aoh->curHero, p.second->slotID), true));
  450. safeRedraw();
  451. }
  452. /**
  453. * Unamarks all slots.
  454. */
  455. void CArtifactsOfHero::unmarkSlots(bool withRedraw /*= true*/)
  456. {
  457. if(commonInfo)
  458. for(CArtifactsOfHero *aoh : commonInfo->participants)
  459. aoh->unmarkLocalSlots(false);
  460. else
  461. unmarkLocalSlots(false);\
  462. if(withRedraw)
  463. safeRedraw();
  464. }
  465. void CArtifactsOfHero::unmarkLocalSlots(bool withRedraw /*= true*/)
  466. {
  467. for(auto p : artWorn)
  468. p.second->selectSlot(false);
  469. for(CHeroArtPlace *place : backpack)
  470. place->selectSlot(false);
  471. if(withRedraw)
  472. safeRedraw();
  473. }
  474. /**
  475. * Assigns an artifacts to an artifact place depending on it's new slot ID.
  476. */
  477. void CArtifactsOfHero::setSlotData(CHeroArtPlace* artPlace, ArtifactPosition slotID)
  478. {
  479. if(!artPlace && slotID >= GameConstants::BACKPACK_START) //spurious call from artifactMoved in attempt to update hidden backpack slot
  480. {
  481. return;
  482. }
  483. artPlace->pickSlot(false);
  484. artPlace->slotID = slotID;
  485. if(const ArtSlotInfo *asi = curHero->getSlot(slotID))
  486. {
  487. artPlace->lockSlot(asi->locked);
  488. artPlace->setArtifact(asi->artifact);
  489. }
  490. else
  491. artPlace->setArtifact(nullptr);
  492. }
  493. /**
  494. * Makes given artifact slot appear as empty with a certain slot ID.
  495. */
  496. void CArtifactsOfHero::eraseSlotData (CHeroArtPlace* artPlace, ArtifactPosition slotID)
  497. {
  498. artPlace->pickSlot(false);
  499. artPlace->slotID = slotID;
  500. artPlace->setArtifact(nullptr);
  501. }
  502. CArtifactsOfHero::CArtifactsOfHero(std::map<ArtifactPosition, CHeroArtPlace *> ArtWorn, std::vector<CHeroArtPlace *> Backpack,
  503. CButton *leftScroll, CButton *rightScroll, bool createCommonPart):
  504. curHero(nullptr),
  505. artWorn(ArtWorn), backpack(Backpack),
  506. backpackPos(0), commonInfo(nullptr), updateState(false),
  507. leftArtRoll(leftScroll), rightArtRoll(rightScroll),
  508. allowedAssembling(true), highlightModeCallback(nullptr)
  509. {
  510. if(createCommonPart)
  511. {
  512. commonInfo = std::make_shared<CArtifactsOfHero::SCommonPart>();
  513. commonInfo->participants.insert(this);
  514. }
  515. // Init slots for worn artifacts.
  516. for (auto p : artWorn)
  517. {
  518. p.second->ourOwner = this;
  519. eraseSlotData(p.second, p.first);
  520. }
  521. // Init slots for the backpack.
  522. for(size_t s=0; s<backpack.size(); ++s)
  523. {
  524. backpack[s]->ourOwner = this;
  525. eraseSlotData(backpack[s], ArtifactPosition(GameConstants::BACKPACK_START + s));
  526. }
  527. leftArtRoll->addCallback(std::bind(&CArtifactsOfHero::scrollBackpack,this,-1));
  528. rightArtRoll->addCallback(std::bind(&CArtifactsOfHero::scrollBackpack,this,+1));
  529. }
  530. CArtifactsOfHero::CArtifactsOfHero(const Point& position, bool createCommonPart /*= false*/)
  531. : curHero(nullptr), backpackPos(0), commonInfo(nullptr), updateState(false), allowedAssembling(true), highlightModeCallback(nullptr)
  532. {
  533. if(createCommonPart)
  534. {
  535. commonInfo = std::make_shared<CArtifactsOfHero::SCommonPart>();
  536. commonInfo->participants.insert(this);
  537. }
  538. OBJ_CONSTRUCTION_CAPTURING_ALL;
  539. pos += position;
  540. std::vector<Point> slotPos =
  541. {
  542. Point(509,30), Point(567,240), Point(509,80), //0-2
  543. Point(383,68), Point(564,183), Point(509,130), //3-5
  544. Point(431,68), Point(610,183), Point(515,295), //6-8
  545. Point(383,143), Point(399,194), Point(415,245), //9-11
  546. Point(431,296), Point(564,30), Point(610,30), //12-14
  547. Point(610,76), Point(610,122), Point(610,310), //15-17
  548. Point(381,296) //18
  549. };
  550. // Create slots for worn artifacts.
  551. for (size_t g = 0; g < GameConstants::BACKPACK_START ; g++)
  552. {
  553. artWorn[ArtifactPosition(g)] = new CHeroArtPlace(slotPos[g]);
  554. artWorn[ArtifactPosition(g)]->ourOwner = this;
  555. eraseSlotData(artWorn[ArtifactPosition(g)], ArtifactPosition(g));
  556. }
  557. // Create slots for the backpack.
  558. for(size_t s=0; s<5; ++s)
  559. {
  560. auto add = new CHeroArtPlace(Point(403 + 46 * s, 365));
  561. add->ourOwner = this;
  562. eraseSlotData(add, ArtifactPosition(GameConstants::BACKPACK_START + s));
  563. backpack.push_back(add);
  564. }
  565. leftArtRoll = new CButton(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [&]{ scrollBackpack(-1);}, SDLK_LEFT);
  566. rightArtRoll = new CButton(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [&]{ scrollBackpack(+1);}, SDLK_RIGHT);
  567. }
  568. CArtifactsOfHero::~CArtifactsOfHero()
  569. {
  570. dispose();
  571. }
  572. void CArtifactsOfHero::updateParentWindow()
  573. {
  574. if (CHeroWindow* chw = dynamic_cast<CHeroWindow*>(GH.topInt()))
  575. {
  576. if(updateState)
  577. chw->curHero = curHero;
  578. else
  579. chw->update(curHero, true);
  580. }
  581. else if(CExchangeWindow* cew = dynamic_cast<CExchangeWindow*>(GH.topInt()))
  582. {
  583. //use our copy of hero to draw window
  584. if(cew->heroInst[0]->id == curHero->id)
  585. cew->heroInst[0] = curHero;
  586. else
  587. cew->heroInst[1] = curHero;
  588. if(!updateState)
  589. {
  590. cew->deactivate();
  591. // for(int g=0; g<ARRAY_COUNT(cew->heroInst); ++g)
  592. // {
  593. // if(cew->heroInst[g] == curHero)
  594. // {
  595. // cew->artifs[g]->setHero(curHero);
  596. // }
  597. // }
  598. cew->prepareBackground();
  599. cew->redraw();
  600. cew->activate();
  601. }
  602. }
  603. }
  604. void CArtifactsOfHero::safeRedraw()
  605. {
  606. if (active)
  607. {
  608. if(parent)
  609. parent->redraw();
  610. else
  611. redraw();
  612. }
  613. }
  614. void CArtifactsOfHero::realizeCurrentTransaction()
  615. {
  616. assert(commonInfo->src.AOH);
  617. assert(commonInfo->dst.AOH);
  618. LOCPLINT->cb->swapArtifacts(ArtifactLocation(commonInfo->src.AOH->curHero, commonInfo->src.slotID),
  619. ArtifactLocation(commonInfo->dst.AOH->curHero, commonInfo->dst.slotID));
  620. }
  621. void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
  622. {
  623. bool isCurHeroSrc = src.isHolder(curHero),
  624. isCurHeroDst = dst.isHolder(curHero);
  625. if(isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
  626. updateSlot(src.slot);
  627. if(isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START)
  628. updateSlot(dst.slot);
  629. if(isCurHeroSrc || isCurHeroDst) //we need to update all slots, artifact might be combined and affect more slots
  630. updateWornSlots(false);
  631. if (!src.isHolder(curHero) && !isCurHeroDst)
  632. return;
  633. if(commonInfo->src == src) //artifact was taken from us
  634. {
  635. assert(commonInfo->dst == dst //expected movement from slot ot slot
  636. || dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START //artifact moved back to backpack (eg. to make place for art we are moving)
  637. || dst.getHolderArtSet()->bearerType() != ArtBearer::HERO);
  638. commonInfo->reset();
  639. unmarkSlots();
  640. }
  641. else if(commonInfo->dst == src) //the dest artifact was moved -> we are picking it
  642. {
  643. assert(dst.slot >= GameConstants::BACKPACK_START);
  644. commonInfo->reset();
  645. CHeroArtPlace *ap = nullptr;
  646. for(CArtifactsOfHero *aoh : commonInfo->participants)
  647. {
  648. if(dst.isHolder(aoh->curHero))
  649. {
  650. commonInfo->src.AOH = aoh;
  651. if((ap = aoh->getArtPlace(dst.slot)))//getArtPlace may return null
  652. break;
  653. }
  654. }
  655. if(ap)
  656. {
  657. ap->select();
  658. }
  659. else
  660. {
  661. commonInfo->src.art = dst.getArt();
  662. commonInfo->src.slotID = dst.slot;
  663. assert(commonInfo->src.AOH);
  664. CCS->curh->dragAndDropCursor(new CAnimImage("artifact", dst.getArt()->artType->iconIndex));
  665. markPossibleSlots(dst.getArt());
  666. }
  667. }
  668. else if(src.slot >= GameConstants::BACKPACK_START &&
  669. src.slot < commonInfo->src.slotID &&
  670. src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
  671. {
  672. //int fixedSlot = src.hero->getArtPos(commonInfo->src.art);
  673. vstd::advance(commonInfo->src.slotID, -1);
  674. assert(commonInfo->src.valid());
  675. }
  676. else
  677. {
  678. //when moving one artifact onto another it leads to two art movements: dst->backapck; src->dst
  679. // however after first movement we pick the art from backpack and the second movement coming when
  680. // we have a different artifact may look surprising... but it's valid.
  681. }
  682. updateParentWindow();
  683. int shift = 0;
  684. // if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos)
  685. // shift++;
  686. //
  687. if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos)
  688. shift++;
  689. if(dst.slot < GameConstants::BACKPACK_START && src.slot - GameConstants::BACKPACK_START < backpackPos)
  690. shift--;
  691. if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
  692. || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) )
  693. scrollBackpack(shift); //update backpack slots
  694. }
  695. void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al)
  696. {
  697. if(al.isHolder(curHero))
  698. {
  699. if(al.slot < GameConstants::BACKPACK_START)
  700. updateWornSlots(0);
  701. else
  702. scrollBackpack(0); //update backpack slots
  703. }
  704. }
  705. CHeroArtPlace * CArtifactsOfHero::getArtPlace(int slot)
  706. {
  707. if(slot < GameConstants::BACKPACK_START)
  708. {
  709. if(artWorn.find(ArtifactPosition(slot)) == artWorn.end())
  710. {
  711. logGlobal->errorStream() << "CArtifactsOfHero::getArtPlace: invalid slot " << slot;
  712. return nullptr;
  713. }
  714. return artWorn[ArtifactPosition(slot)];
  715. }
  716. else
  717. {
  718. for(CHeroArtPlace *ap : backpack)
  719. if(ap->slotID == slot)
  720. return ap;
  721. return nullptr;
  722. }
  723. }
  724. void CArtifactsOfHero::artifactAssembled(const ArtifactLocation &al)
  725. {
  726. if(al.isHolder(curHero))
  727. updateWornSlots();
  728. }
  729. void CArtifactsOfHero::artifactDisassembled(const ArtifactLocation &al)
  730. {
  731. if(al.isHolder(curHero))
  732. updateWornSlots();
  733. }
  734. void CArtifactsOfHero::updateWornSlots(bool redrawParent /*= true*/)
  735. {
  736. for(auto p : artWorn)
  737. updateSlot(p.first);
  738. if(redrawParent)
  739. updateParentWindow();
  740. }
  741. const CGHeroInstance * CArtifactsOfHero::getHero() const
  742. {
  743. return curHero;
  744. }
  745. void CArtifactsOfHero::updateSlot(ArtifactPosition slotID)
  746. {
  747. setSlotData(getArtPlace(slotID), slotID);
  748. }
  749. CArtifactHolder::CArtifactHolder()
  750. {
  751. }
  752. void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation &artLoc)
  753. {
  754. for(CArtifactsOfHero *aoh : artSets)
  755. aoh->artifactRemoved(artLoc);
  756. }
  757. void CWindowWithArtifacts::artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)
  758. {
  759. CArtifactsOfHero *destaoh = nullptr;
  760. for(CArtifactsOfHero *aoh : artSets)
  761. {
  762. aoh->artifactMoved(artLoc, destLoc);
  763. aoh->redraw();
  764. if(destLoc.isHolder(aoh->getHero()))
  765. destaoh = aoh;
  766. }
  767. //Make sure the status bar is updated so it does not display old text
  768. if(destaoh != nullptr && destaoh->getArtPlace(destLoc.slot) != nullptr)
  769. {
  770. destaoh->getArtPlace(destLoc.slot)->hover(true);
  771. }
  772. }
  773. void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation &artLoc)
  774. {
  775. for(CArtifactsOfHero *aoh : artSets)
  776. aoh->artifactDisassembled(artLoc);
  777. }
  778. void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation &artLoc)
  779. {
  780. for(CArtifactsOfHero *aoh : artSets)
  781. aoh->artifactAssembled(artLoc);
  782. }
  783. void CArtifactsOfHero::SCommonPart::Artpos::clear()
  784. {
  785. slotID = ArtifactPosition::PRE_FIRST;
  786. AOH = nullptr;
  787. art = nullptr;
  788. }
  789. CArtifactsOfHero::SCommonPart::Artpos::Artpos()
  790. {
  791. clear();
  792. }
  793. void CArtifactsOfHero::SCommonPart::Artpos::setTo(const CHeroArtPlace *place, bool dontTakeBackpack)
  794. {
  795. slotID = place->slotID;
  796. AOH = place->ourOwner;
  797. if(slotID >= 19 && dontTakeBackpack)
  798. art = nullptr;
  799. else
  800. art = place->ourArt;
  801. }
  802. bool CArtifactsOfHero::SCommonPart::Artpos::operator==(const ArtifactLocation &al) const
  803. {
  804. if(!AOH)
  805. return false;
  806. bool ret = al.isHolder(AOH->curHero) && al.slot == slotID;
  807. //assert(al.getArt() == art);
  808. return ret;
  809. }
  810. bool CArtifactsOfHero::SCommonPart::Artpos::valid()
  811. {
  812. assert(AOH && art);
  813. return art == AOH->curHero->getArt(slotID);
  814. }
  815. CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art) : ourArt(Art)
  816. {
  817. image = nullptr;
  818. pos += position;
  819. pos.w = pos.h = 44;
  820. }
  821. void CArtPlace::clickLeft(tribool down, bool previousState)
  822. {
  823. LRClickableAreaWTextComp::clickLeft(down, previousState);
  824. }
  825. void CArtPlace::clickRight(tribool down, bool previousState)
  826. {
  827. LRClickableAreaWTextComp::clickRight(down, previousState);
  828. }
  829. CCommanderArtPlace::CCommanderArtPlace(Point position, const CGHeroInstance * commanderOwner, ArtifactPosition artSlot, const CArtifactInstance * Art) : CArtPlace(position, Art), commanderOwner(commanderOwner), commanderSlotID(artSlot.num)
  830. {
  831. createImage();
  832. setArtifact(Art);
  833. }
  834. void CCommanderArtPlace::clickLeft(tribool down, bool previousState)
  835. {
  836. if (ourArt && text.size() && down)
  837. LOCPLINT->showYesNoDialog(CGI->generaltexth->localizedTexts["commanderWindow"]["artifactMessage"].String(), [this] { returnArtToHeroCallback(); }, [] {});
  838. }
  839. void CCommanderArtPlace::clickRight(tribool down, bool previousState)
  840. {
  841. if (ourArt && text.size() && down)
  842. CArtPlace::clickRight(down, previousState);
  843. }
  844. void CCommanderArtPlace::createImage()
  845. {
  846. OBJ_CONSTRUCTION_CAPTURING_ALL;
  847. int imageIndex = 0;
  848. if (ourArt)
  849. imageIndex = ourArt->artType->iconIndex;
  850. image = new CAnimImage("artifact", imageIndex);
  851. if (!ourArt)
  852. image->disable();
  853. }
  854. void CCommanderArtPlace::returnArtToHeroCallback()
  855. {
  856. ArtifactPosition artifactPos = commanderSlotID;;
  857. ArtifactPosition freeSlot = ourArt->firstBackpackSlot(commanderOwner);
  858. ArtifactLocation src(commanderOwner->commander.get(), artifactPos);
  859. ArtifactLocation dst(commanderOwner, freeSlot);
  860. if (ourArt->canBePutAt(dst, true))
  861. {
  862. LOCPLINT->cb->swapArtifacts(src, dst);
  863. setArtifact(nullptr);
  864. parent->redraw();
  865. }
  866. }
  867. void CCommanderArtPlace::setArtifact(const CArtifactInstance * art)
  868. {
  869. baseType = -1; //by default we don't store any component
  870. ourArt = art;
  871. if (!art)
  872. {
  873. image->disable();
  874. text = std::string();
  875. return;
  876. }
  877. image->enable();
  878. image->setFrame(art->artType->iconIndex);
  879. text = art->getEffectiveDescription();
  880. if (art->artType->id == ArtifactID::SPELL_SCROLL)
  881. {
  882. int spellID = art->getGivenSpellID();
  883. if (spellID >= 0)
  884. {
  885. //add spell component info (used to provide a pic in r-click popup)
  886. baseType = CComponent::spell;
  887. type = spellID;
  888. bonusValue = 0;
  889. }
  890. }
  891. else
  892. {
  893. baseType = CComponent::artifact;
  894. type = art->artType->id;
  895. bonusValue = 0;
  896. }
  897. }