CGarrisonInt.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /*
  2. * CGarrisonInt.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 "CGarrisonInt.h"
  12. #include "Buttons.h"
  13. #include "TextControls.h"
  14. #include "../gui/CGuiHandler.h"
  15. #include "../renderSDL/SDL_Extensions.h"
  16. #include "../windows/CCreatureWindow.h"
  17. #include "../windows/GUIClasses.h"
  18. #include "../CGameInfo.h"
  19. #include "../CPlayerInterface.h"
  20. #include "../../CCallback.h"
  21. #include "../../lib/CGeneralTextHandler.h"
  22. #include "../../lib/CCreatureHandler.h"
  23. #include "../../lib/mapObjects/CGHeroInstance.h"
  24. #include "../../lib/CGameState.h"
  25. void CGarrisonSlot::setHighlight(bool on)
  26. {
  27. if (on)
  28. selectionImage->enable(); //show
  29. else
  30. selectionImage->disable(); //hide
  31. }
  32. void CGarrisonSlot::hover (bool on)
  33. {
  34. ////Hoverable::hover(on);
  35. if(on)
  36. {
  37. std::string temp;
  38. if(creature)
  39. {
  40. if(owner->getSelection())
  41. {
  42. if(owner->getSelection() == this)
  43. {
  44. temp = CGI->generaltexth->tcommands[4]; //View %s
  45. boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
  46. }
  47. else if (owner->getSelection()->creature == creature)
  48. {
  49. temp = CGI->generaltexth->tcommands[2]; //Combine %s armies
  50. boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
  51. }
  52. else if (owner->getSelection()->creature)
  53. {
  54. temp = CGI->generaltexth->tcommands[7]; //Exchange %s with %s
  55. boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->getNameSingularTranslated());
  56. boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
  57. }
  58. else
  59. {
  60. logGlobal->warn("Warning - shouldn't be - highlighted void slot %d", owner->getSelection()->ID.getNum());
  61. logGlobal->warn("Highlighted set to nullptr");
  62. owner->selectSlot(nullptr);
  63. }
  64. }
  65. else
  66. {
  67. const bool isHeroOnMap = owner->armedObjs[0] // Hero is not a visitor and not a garrison defender
  68. && owner->armedObjs[0]->ID == Obj::HERO
  69. && (!owner->armedObjs[1] || owner->armedObjs[1]->ID == Obj::HERO) // one hero or we are in the Heroes exchange window
  70. && !(static_cast<const CGHeroInstance*>(owner->armedObjs[0]))->inTownGarrison;
  71. if(isHeroOnMap)
  72. {
  73. temp = CGI->generaltexth->allTexts[481]; //Select %s
  74. }
  75. else if(upg == EGarrisonType::UP)
  76. {
  77. temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
  78. }
  79. else // Hero is visiting some object (town, mine, etc)
  80. {
  81. temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
  82. }
  83. boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
  84. }
  85. }
  86. else
  87. {
  88. if(owner->getSelection())
  89. {
  90. const CArmedInstance *highl = owner->getSelection()->getObj();
  91. if( highl->needsLastStack() //we are moving stack from hero's
  92. && highl->stacksCount() == 1 //it's only stack
  93. && owner->getSelection()->upg != upg //we're moving it to the other garrison
  94. )
  95. {
  96. temp = CGI->generaltexth->tcommands[5]; //Cannot move last army to garrison
  97. }
  98. else
  99. {
  100. temp = CGI->generaltexth->tcommands[6]; //Move %s
  101. boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->getNameSingularTranslated());
  102. }
  103. }
  104. else
  105. {
  106. temp = CGI->generaltexth->tcommands[11]; //Empty
  107. }
  108. }
  109. GH.statusbar->write(temp);
  110. }
  111. else
  112. {
  113. GH.statusbar->clear();
  114. }
  115. }
  116. const CArmedInstance * CGarrisonSlot::getObj() const
  117. {
  118. return owner->armedObjs[upg];
  119. }
  120. /// @return Whether the unit in the slot belongs to the current player.
  121. bool CGarrisonSlot::our() const
  122. {
  123. return owner->owned[upg];
  124. }
  125. /// @return Whether the unit in the slot belongs to an ally but not to the current player.
  126. bool CGarrisonSlot::ally() const
  127. {
  128. if(!getObj())
  129. return false;
  130. return PlayerRelations::ALLIES == LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, getObj()->tempOwner);
  131. }
  132. std::function<void()> CGarrisonSlot::getDismiss() const
  133. {
  134. const bool canDismiss = getObj()->tempOwner == LOCPLINT->playerID
  135. && (getObj()->stacksCount() > 1 ||
  136. !getObj()->needsLastStack());
  137. return canDismiss ? [=]()
  138. {
  139. LOCPLINT->cb->dismissCreature(getObj(), ID);
  140. } : (std::function<void()>)nullptr;
  141. }
  142. /// The creature slot has been clicked twice, therefore the creature info should be shown
  143. /// @return Whether the view should be refreshed
  144. bool CGarrisonSlot::viewInfo()
  145. {
  146. UpgradeInfo pom;
  147. LOCPLINT->cb->fillUpgradeInfo(getObj(), ID, pom);
  148. bool canUpgrade = getObj()->tempOwner == LOCPLINT->playerID && pom.oldID>=0; //upgrade is possible
  149. std::function<void(CreatureID)> upgr = nullptr;
  150. auto dism = getDismiss();
  151. if(canUpgrade) upgr = [=] (CreatureID newID) { LOCPLINT->cb->upgradeCreature(getObj(), ID, newID); };
  152. owner->selectSlot(nullptr);
  153. owner->setSplittingMode(false);
  154. for(auto & elem : owner->splitButtons)
  155. elem->block(true);
  156. redraw();
  157. GH.pushIntT<CStackWindow>(myStack, dism, pom, upgr);
  158. return true;
  159. }
  160. /// The selection is empty, therefore the creature should be moved
  161. /// @return Whether the view should be refreshed
  162. bool CGarrisonSlot::highlightOrDropArtifact()
  163. {
  164. bool artSelected = false;
  165. if (CWindowWithArtifacts* chw = dynamic_cast<CWindowWithArtifacts*>(GH.topInt().get())) //dirty solution
  166. {
  167. std::shared_ptr<CArtifactsOfHero::SCommonPart> commonInfo = chw->getCommonPart();
  168. const CArtifactInstance * art = nullptr;
  169. if(commonInfo)
  170. art = commonInfo->src.art;
  171. if(art)
  172. {
  173. const CGHeroInstance *srcHero = commonInfo->src.AOH->getHero();
  174. artSelected = true;
  175. if (myStack) // try dropping the artifact only if the slot isn't empty
  176. {
  177. ArtifactLocation src(srcHero, commonInfo->src.slotID);
  178. ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT);
  179. if (art->canBePutAt(dst, true))
  180. { //equip clicked stack
  181. if(dst.getArt())
  182. {
  183. //creature can wear only one active artifact
  184. //if we are placing a new one, the old one will be returned to the hero's backpack
  185. LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero, dst.getArt()->firstBackpackSlot(srcHero)));
  186. }
  187. LOCPLINT->cb->swapArtifacts(src, dst);
  188. }
  189. }
  190. }
  191. }
  192. if (!artSelected && creature)
  193. {
  194. owner->selectSlot(this);
  195. if(creature)
  196. {
  197. for(auto & elem : owner->splitButtons)
  198. elem->block(!our());
  199. }
  200. }
  201. redraw();
  202. return true;
  203. }
  204. /// The creature is only being partially moved
  205. /// @return Whether the view should be refreshed
  206. bool CGarrisonSlot::split()
  207. {
  208. const CGarrisonSlot * selection = owner->getSelection();
  209. owner->p2 = ID; // store the second stack pos
  210. owner->pb = upg; // store the second stack owner (up or down army)
  211. owner->setSplittingMode(false);
  212. int minLeft=0, minRight=0;
  213. if(upg != selection->upg) // not splitting within same army
  214. {
  215. if(selection->getObj()->stacksCount() == 1 // we're splitting away the last stack
  216. && selection->getObj()->needsLastStack() )
  217. {
  218. minLeft = 1;
  219. }
  220. // destination army can't be emptied, unless we're rebalancing two stacks of same creature
  221. if(getObj()->stacksCount() == 1
  222. && selection->creature == creature
  223. && getObj()->needsLastStack() )
  224. {
  225. minRight = 1;
  226. }
  227. }
  228. int countLeft = selection->myStack ? selection->myStack->count : 0;
  229. int countRight = myStack ? myStack->count : 0;
  230. GH.pushIntT<CSplitWindow>(selection->creature, std::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
  231. minLeft, minRight, countLeft, countRight);
  232. return true;
  233. }
  234. /// If certain creates cannot be moved, the selection should change
  235. /// Force reselection in these cases
  236. /// * When attempting to take creatures from ally
  237. /// * When attempting to swap creatures with an ally
  238. /// * When attempting to take unremovable units
  239. /// @return Whether reselection must be done
  240. bool CGarrisonSlot::mustForceReselection() const
  241. {
  242. const CGarrisonSlot * selection = owner->getSelection();
  243. bool withAlly = selection->our() ^ our();
  244. if (!creature || !selection->creature)
  245. return false;
  246. // Attempt to take creatures from ally (select theirs first)
  247. if (!selection->our())
  248. return true;
  249. // Attempt to swap creatures with ally (select ours first)
  250. if (selection->creature != creature && withAlly)
  251. return true;
  252. if (!owner->removableUnits)
  253. {
  254. if (selection->upg == EGarrisonType::UP)
  255. return true;
  256. else
  257. return creature || upg == EGarrisonType::UP;
  258. }
  259. return false;
  260. }
  261. void CGarrisonSlot::clickRight(tribool down, bool previousState)
  262. {
  263. if(creature && down)
  264. {
  265. GH.pushIntT<CStackWindow>(myStack, true);
  266. }
  267. }
  268. void CGarrisonSlot::clickLeft(tribool down, bool previousState)
  269. {
  270. if(down)
  271. {
  272. bool refr = false;
  273. const CGarrisonSlot * selection = owner->getSelection();
  274. if(!selection)
  275. {
  276. refr = highlightOrDropArtifact(); // Affects selection
  277. handleSplittingShortcuts();
  278. }
  279. else if(selection == this)
  280. {
  281. if(!handleSplittingShortcuts())
  282. refr = viewInfo(); // Affects selection
  283. }
  284. // Re-highlight if troops aren't removable or not ours.
  285. else if (mustForceReselection())
  286. {
  287. if(creature)
  288. owner->selectSlot(this);
  289. redraw();
  290. refr = true;
  291. }
  292. else
  293. {
  294. const CArmedInstance * selectedObj = owner->armedObjs[selection->upg];
  295. bool lastHeroStackSelected = false;
  296. if(selectedObj->stacksCount() == 1
  297. && owner->getSelection()->upg != upg
  298. && selectedObj->needsLastStack())
  299. {
  300. lastHeroStackSelected = true;
  301. }
  302. if((owner->getSplittingMode() || GH.isKeyboardShiftDown()) // split window
  303. && (!creature || creature == selection->creature))
  304. {
  305. refr = split();
  306. }
  307. else if(!creature && lastHeroStackSelected) // split all except last creature
  308. LOCPLINT->cb->splitStack(selectedObj, owner->armedObjs[upg], selection->ID, ID, selection->myStack->count - 1);
  309. else if(creature != selection->creature) // swap
  310. LOCPLINT->cb->swapCreatures(owner->armedObjs[upg], selectedObj, ID, selection->ID);
  311. else if(lastHeroStackSelected) // merge last stack to other hero stack
  312. refr = split();
  313. else // merge
  314. LOCPLINT->cb->mergeStacks(selectedObj, owner->armedObjs[upg], selection->ID, ID);
  315. }
  316. if(refr)
  317. {
  318. // Refresh Statusbar
  319. hover(false);
  320. hover(true);
  321. }
  322. }
  323. }
  324. void CGarrisonSlot::update()
  325. {
  326. if(getObj() != nullptr)
  327. {
  328. addUsedEvents(LCLICK | RCLICK | HOVER);
  329. myStack = getObj()->getStackPtr(ID);
  330. creature = myStack ? myStack->type : nullptr;
  331. }
  332. else
  333. {
  334. removeUsedEvents(LCLICK | RCLICK | HOVER);
  335. myStack = nullptr;
  336. creature = nullptr;
  337. }
  338. if(creature)
  339. {
  340. creatureImage->enable();
  341. creatureImage->setFrame(creature->getIconIndex());
  342. stackCount->enable();
  343. stackCount->setText(vstd::formatMetric(myStack->count, 4));
  344. }
  345. else
  346. {
  347. creatureImage->disable();
  348. stackCount->disable();
  349. }
  350. }
  351. CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, CGarrisonSlot::EGarrisonType Upg, const CStackInstance * creature_)
  352. : ID(IID),
  353. owner(Owner),
  354. myStack(creature_),
  355. creature(creature_ ? creature_->type : nullptr),
  356. upg(Upg)
  357. {
  358. OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
  359. pos.x += x;
  360. pos.y += y;
  361. std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
  362. creatureImage = std::make_shared<CAnimImage>(imgName, 0);
  363. creatureImage->disable();
  364. selectionImage = std::make_shared<CAnimImage>(imgName, 1);
  365. selectionImage->disable();
  366. if(Owner->smallIcons)
  367. {
  368. pos.w = 32;
  369. pos.h = 32;
  370. }
  371. else
  372. {
  373. pos.w = 58;
  374. pos.h = 64;
  375. }
  376. stackCount = std::make_shared<CLabel>(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, ETextAlignment::BOTTOMRIGHT, Colors::WHITE);
  377. update();
  378. }
  379. void CGarrisonSlot::splitIntoParts(CGarrisonSlot::EGarrisonType type, int amount)
  380. {
  381. auto empty = owner->getEmptySlot(type);
  382. if(empty == SlotID())
  383. return;
  384. owner->pb = type;
  385. owner->p2 = empty;
  386. owner->splitStacks(1, amount);
  387. }
  388. bool CGarrisonSlot::handleSplittingShortcuts()
  389. {
  390. const bool isAlt = GH.isKeyboardAltDown();
  391. const bool isLShift = GH.isKeyboardShiftDown();
  392. const bool isLCtrl = GH.isKeyboardCtrlDown();
  393. if(!isAlt && !isLShift && !isLCtrl)
  394. return false; // This is only case when return false
  395. auto selected = owner->getSelection();
  396. if(!selected)
  397. return true; // Some Shortcusts are pressed but there are no appropriate actions
  398. auto units = selected->myStack->count;
  399. if(units < 1)
  400. return true;
  401. if (isLShift && isLCtrl && isAlt)
  402. {
  403. owner->bulkMoveArmy(selected);
  404. }
  405. else if(isLCtrl && isAlt)
  406. {
  407. owner->moveStackToAnotherArmy(selected);
  408. }
  409. else if(isLShift && isAlt)
  410. {
  411. auto dismiss = getDismiss();
  412. if(dismiss)
  413. LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[12], dismiss, nullptr);
  414. }
  415. else if(isAlt)
  416. {
  417. owner->bulkMergeStacks(selected);
  418. }
  419. else
  420. {
  421. if(units <= 1)
  422. return true;
  423. if(isLCtrl && isLShift)
  424. owner->bulkSplitStack(selected);
  425. else if(isLShift)
  426. owner->bulkSmartSplitStack(selected);
  427. else
  428. splitIntoParts(selected->upg, 1); // LCtrl
  429. }
  430. return true;
  431. }
  432. void CGarrisonInt::addSplitBtn(std::shared_ptr<CButton> button)
  433. {
  434. addChild(button.get());
  435. button->recActions &= ~DISPOSE;
  436. splitButtons.push_back(button);
  437. button->block(getSelection() == nullptr);
  438. }
  439. void CGarrisonInt::createSlots()
  440. {
  441. int distance = interx + (smallIcons ? 32 : 58);
  442. for(int i=0; i<2; i++)
  443. {
  444. std::vector<std::shared_ptr<CGarrisonSlot>> garrisonSlots;
  445. garrisonSlots.resize(7);
  446. if(armedObjs[i])
  447. {
  448. for(auto & elem : armedObjs[i]->Slots())
  449. {
  450. garrisonSlots[elem.first.getNum()] = std::make_shared<CGarrisonSlot>(this, i*garOffset.x + (elem.first.getNum()*distance), i*garOffset.y, elem.first, static_cast<CGarrisonSlot::EGarrisonType>(i), elem.second);
  451. }
  452. }
  453. for(int j=0; j<7; j++)
  454. {
  455. if(!garrisonSlots[j])
  456. garrisonSlots[j] = std::make_shared<CGarrisonSlot>(this, i*garOffset.x + (j*distance), i*garOffset.y, SlotID(j), static_cast<CGarrisonSlot::EGarrisonType>(i), nullptr);
  457. if(twoRows && j>=4)
  458. {
  459. garrisonSlots[j]->moveBy(Point(-126, 37));
  460. }
  461. }
  462. vstd::concatenate(availableSlots, garrisonSlots);
  463. }
  464. }
  465. void CGarrisonInt::recreateSlots()
  466. {
  467. selectSlot(nullptr);
  468. setSplittingMode(false);
  469. for(auto & elem : splitButtons)
  470. elem->block(true);
  471. for(auto slot : availableSlots)
  472. slot->update();
  473. }
  474. void CGarrisonInt::splitClick()
  475. {
  476. if(!getSelection())
  477. return;
  478. setSplittingMode(!getSplittingMode());
  479. redraw();
  480. }
  481. void CGarrisonInt::splitStacks(int, int amountRight)
  482. {
  483. LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight);
  484. }
  485. bool CGarrisonInt::checkSelected(const CGarrisonSlot * selected, TQuantity min) const
  486. {
  487. return selected && selected->myStack && selected->myStack->count > min && selected->creature;
  488. }
  489. void CGarrisonInt::moveStackToAnotherArmy(const CGarrisonSlot * selected)
  490. {
  491. if(!checkSelected(selected))
  492. return;
  493. const auto srcArmyType = selected->upg;
  494. const auto destArmyType = srcArmyType == CGarrisonSlot::UP
  495. ? CGarrisonSlot::DOWN
  496. : CGarrisonSlot::UP;
  497. auto srcArmy = armedObjs[srcArmyType];
  498. auto destArmy = armedObjs[destArmyType];
  499. if(!destArmy)
  500. return;
  501. auto destSlot = destArmy->getSlotFor(selected->creature);
  502. if(destSlot == SlotID())
  503. return;
  504. const auto srcSlot = selected->ID;
  505. const bool isDestSlotEmpty = !destArmy->getStackCount(destSlot);
  506. if(isDestSlotEmpty && !destArmy->getStackCount(srcSlot))
  507. destSlot = srcSlot; // Same place is more preferable
  508. const bool isLastStack = srcArmy->stacksCount() == 1 && srcArmy->needsLastStack();
  509. auto srcAmount = selected->myStack->count - (isLastStack ? 1 : 0);
  510. if(!srcAmount)
  511. return;
  512. if(!isDestSlotEmpty || isLastStack)
  513. {
  514. srcAmount += destArmy->getStackCount(destSlot); // Due to 'split' implementation in the 'CGameHandler::arrangeStacks'
  515. LOCPLINT->cb->splitStack(srcArmy, destArmy, srcSlot, destSlot, srcAmount);
  516. }
  517. else
  518. {
  519. LOCPLINT->cb->swapCreatures(srcArmy, destArmy, srcSlot, destSlot);
  520. }
  521. }
  522. void CGarrisonInt::bulkMoveArmy(const CGarrisonSlot * selected)
  523. {
  524. if(!checkSelected(selected))
  525. return;
  526. const auto srcArmyType = selected->upg;
  527. const auto destArmyType = (srcArmyType == CGarrisonSlot::UP)
  528. ? CGarrisonSlot::DOWN
  529. : CGarrisonSlot::UP;
  530. auto srcArmy = armedObjs[srcArmyType];
  531. auto destArmy = armedObjs[destArmyType];
  532. if(!destArmy)
  533. return;
  534. const auto srcSlot = selected->ID;
  535. LOCPLINT->cb->bulkMoveArmy(srcArmy->id, destArmy->id, srcSlot);
  536. }
  537. void CGarrisonInt::bulkMergeStacks(const CGarrisonSlot * selected)
  538. {
  539. if(!checkSelected(selected))
  540. return;
  541. const auto type = selected->upg;
  542. if(!armedObjs[type]->hasCreatureSlots(selected->creature, selected->ID))
  543. return;
  544. LOCPLINT->cb->bulkMergeStacks(armedObjs[type]->id, selected->ID);
  545. }
  546. void CGarrisonInt::bulkSplitStack(const CGarrisonSlot * selected)
  547. {
  548. if(!checkSelected(selected, 1)) // check if > 1
  549. return;
  550. const auto type = selected->upg;
  551. if(!hasEmptySlot(type))
  552. return;
  553. LOCPLINT->cb->bulkSplitStack(armedObjs[type]->id, selected->ID);
  554. }
  555. void CGarrisonInt::bulkSmartSplitStack(const CGarrisonSlot * selected)
  556. {
  557. if(!checkSelected(selected, 1))
  558. return;
  559. const auto type = selected->upg;
  560. // Do not disturb the server if the creature is already balanced
  561. if(!hasEmptySlot(type) && armedObjs[type]->isCreatureBalanced(selected->creature))
  562. return;
  563. LOCPLINT->cb->bulkSmartSplitStack(armedObjs[type]->id, selected->ID);
  564. }
  565. CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset,
  566. const CArmedInstance * s1, const CArmedInstance * s2,
  567. bool _removableUnits, bool smallImgs, bool _twoRows)
  568. : highlighted(nullptr),
  569. inSplittingMode(false),
  570. interx(inx),
  571. garOffset(garsOffset),
  572. pb(false),
  573. smallIcons(smallImgs),
  574. removableUnits(_removableUnits),
  575. twoRows(_twoRows)
  576. {
  577. OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
  578. setArmy(s1, false);
  579. setArmy(s2, true);
  580. pos.x += x;
  581. pos.y += y;
  582. createSlots();
  583. }
  584. const CGarrisonSlot * CGarrisonInt::getSelection() const
  585. {
  586. return highlighted;
  587. }
  588. void CGarrisonInt::selectSlot(CGarrisonSlot *slot)
  589. {
  590. if(slot != highlighted)
  591. {
  592. if(highlighted)
  593. highlighted->setHighlight(false);
  594. highlighted = slot;
  595. for(auto button : splitButtons)
  596. button->block(highlighted == nullptr || !slot->our());
  597. if(highlighted)
  598. highlighted->setHighlight(true);
  599. }
  600. }
  601. void CGarrisonInt::setSplittingMode(bool on)
  602. {
  603. assert(on == false || highlighted != nullptr); //can't be in splitting mode without selection
  604. if(inSplittingMode || on)
  605. {
  606. for(auto slot : availableSlots)
  607. {
  608. if(slot.get() != getSelection())
  609. slot->setHighlight( ( on && (slot->our() || slot->ally()) && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
  610. }
  611. inSplittingMode = on;
  612. }
  613. }
  614. bool CGarrisonInt::getSplittingMode()
  615. {
  616. return inSplittingMode;
  617. }
  618. SlotID CGarrisonInt::getEmptySlot(CGarrisonSlot::EGarrisonType type) const
  619. {
  620. assert(armedObjs[type]);
  621. return armedObjs[type] ? armedObjs[type]->getFreeSlot() : SlotID();
  622. }
  623. bool CGarrisonInt::hasEmptySlot(CGarrisonSlot::EGarrisonType type) const
  624. {
  625. return getEmptySlot(type) != SlotID();
  626. }
  627. void CGarrisonInt::setArmy(const CArmedInstance * army, bool bottomGarrison)
  628. {
  629. owned[bottomGarrison] = army ? (army->tempOwner == LOCPLINT->playerID || army->tempOwner == PlayerColor::UNFLAGGABLE) : false;
  630. armedObjs[bottomGarrison] = army;
  631. }