CGarrisonInt.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. #include "StdInc.h"
  2. #include "CGarrisonInt.h"
  3. #include "../gui/CGuiHandler.h"
  4. #include "CAnimation.h"
  5. #include "../CGameInfo.h"
  6. #include "../CPlayerInterface.h"
  7. #include "../windows/CCreatureWindow.h"
  8. #include "../windows/GUIClasses.h"
  9. #include "../../CCallback.h"
  10. #include "../../lib/CGeneralTextHandler.h"
  11. #include "../../lib/CCreatureHandler.h"
  12. #include "../../lib/mapObjects/CGHeroInstance.h"
  13. #include "../../lib/CGameState.h"
  14. void CGarrisonSlot::setHighlight(bool on)
  15. {
  16. if (on)
  17. selectionImage->enable(); //show
  18. else
  19. selectionImage->disable(); //hide
  20. }
  21. void CGarrisonSlot::hover (bool on)
  22. {
  23. ////Hoverable::hover(on);
  24. if(on)
  25. {
  26. std::string temp;
  27. if(creature)
  28. {
  29. if(owner->getSelection())
  30. {
  31. if(owner->getSelection() == this)
  32. {
  33. temp = CGI->generaltexth->tcommands[4]; //View %s
  34. boost::algorithm::replace_first(temp,"%s",creature->nameSing);
  35. }
  36. else if (owner->getSelection()->creature == creature)
  37. {
  38. temp = CGI->generaltexth->tcommands[2]; //Combine %s armies
  39. boost::algorithm::replace_first(temp,"%s",creature->nameSing);
  40. }
  41. else if (owner->getSelection()->creature)
  42. {
  43. temp = CGI->generaltexth->tcommands[7]; //Exchange %s with %s
  44. boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
  45. boost::algorithm::replace_first(temp,"%s",creature->nameSing);
  46. }
  47. else
  48. {
  49. logGlobal->warnStream() << "Warning - shouldn't be - highlighted void slot "<<owner->getSelection();
  50. logGlobal->warnStream() << "Highlighted set to nullptr";
  51. owner->selectSlot(nullptr);
  52. }
  53. }
  54. else
  55. {
  56. if(upg)
  57. {
  58. temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
  59. }
  60. else if(owner->armedObjs[0] && owner->armedObjs[0]->ID == Obj::TOWN)
  61. {
  62. temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
  63. }
  64. else
  65. {
  66. temp = CGI->generaltexth->allTexts[481]; //Select %s
  67. }
  68. boost::algorithm::replace_first(temp,"%s",creature->nameSing);
  69. };
  70. }
  71. else
  72. {
  73. if(owner->getSelection())
  74. {
  75. const CArmedInstance *highl = owner->getSelection()->getObj();
  76. if( highl->needsLastStack() //we are moving stack from hero's
  77. && highl->stacksCount() == 1 //it's only stack
  78. && owner->getSelection()->upg != upg //we're moving it to the other garrison
  79. )
  80. {
  81. temp = CGI->generaltexth->tcommands[5]; //Cannot move last army to garrison
  82. }
  83. else
  84. {
  85. temp = CGI->generaltexth->tcommands[6]; //Move %s
  86. boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
  87. }
  88. }
  89. else
  90. {
  91. temp = CGI->generaltexth->tcommands[11]; //Empty
  92. }
  93. }
  94. GH.statusbar->setText(temp);
  95. }
  96. else
  97. {
  98. GH.statusbar->clear();
  99. }
  100. }
  101. const CArmedInstance * CGarrisonSlot::getObj() const
  102. {
  103. return (!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]);
  104. }
  105. bool CGarrisonSlot::our() const
  106. {
  107. return upg?(owner->owned[1]):(owner->owned[0]);
  108. }
  109. void CGarrisonSlot::clickRight(tribool down, bool previousState)
  110. {
  111. if(down && creature)
  112. {
  113. GH.pushInt(new CStackWindow(myStack, true));
  114. }
  115. }
  116. void CGarrisonSlot::clickLeft(tribool down, bool previousState)
  117. {
  118. if(down)
  119. {
  120. bool refr = false;
  121. if(owner->getSelection())
  122. {
  123. if(owner->getSelection() == this) //view info
  124. {
  125. UpgradeInfo pom;
  126. LOCPLINT->cb->getUpgradeInfo(getObj(), ID, pom);
  127. bool canUpgrade = getObj()->tempOwner == LOCPLINT->playerID && pom.oldID>=0; //upgrade is possible
  128. bool canDismiss = getObj()->tempOwner == LOCPLINT->playerID && (getObj()->stacksCount()>1 || !getObj()->needsLastStack());
  129. std::function<void(CreatureID)> upgr = nullptr;
  130. std::function<void()> dism = nullptr;
  131. if(canUpgrade) upgr = [=] (CreatureID newID) { LOCPLINT->cb->upgradeCreature(getObj(), ID, newID); };
  132. if(canDismiss) dism = [=] { LOCPLINT->cb->dismissCreature(getObj(), ID); };
  133. owner->selectSlot(nullptr);
  134. owner->setSplittingMode(false);
  135. for(auto & elem : owner->splitButtons)
  136. elem->block(true);
  137. redraw();
  138. refr = true;
  139. GH.pushInt(new CStackWindow(myStack, dism, pom, upgr));
  140. }
  141. else
  142. {
  143. // Only allow certain moves if troops aren't removable or not ours.
  144. if ( ( owner->getSelection()->our()//our creature is selected
  145. || owner->getSelection()->creature == creature )//or we are rebalancing army
  146. && ( owner->removableUnits
  147. || (upg == 0 && ( owner->getSelection()->upg == 1 && !creature ) )
  148. || (upg == 1 && owner->getSelection()->upg == 1 ) ) )
  149. {
  150. //we want to split
  151. if((owner->getSplittingMode() || LOCPLINT->shiftPressed())
  152. && (!creature
  153. || (creature == owner->getSelection()->creature)))
  154. {
  155. owner->p2 = ID; //store the second stack pos
  156. owner->pb = upg;//store the second stack owner (up or down army)
  157. owner->setSplittingMode(false);
  158. int minLeft=0, minRight=0;
  159. if(upg != owner->getSelection()->upg) //not splitting within same army
  160. {
  161. if(owner->getSelection()->getObj()->stacksCount() == 1 //we're splitting away the last stack
  162. && owner->getSelection()->getObj()->needsLastStack() )
  163. {
  164. minLeft = 1;
  165. }
  166. if(getObj()->stacksCount() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
  167. && owner->getSelection()->creature == creature
  168. && getObj()->needsLastStack() )
  169. {
  170. minRight = 1;
  171. }
  172. }
  173. int countLeft = owner->getSelection()->myStack ? owner->getSelection()->myStack->count : 0;
  174. int countRight = myStack ? myStack->count : 0;
  175. GH.pushInt(new CSplitWindow(owner->getSelection()->creature, boost::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
  176. minLeft, minRight, countLeft, countRight));
  177. refr = true;
  178. }
  179. else if(creature != owner->getSelection()->creature) //swap
  180. {
  181. LOCPLINT->cb->swapCreatures(
  182. (!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  183. (!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  184. ID,owner->getSelection()->ID);
  185. }
  186. else //merge
  187. {
  188. LOCPLINT->cb->mergeStacks(
  189. (!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  190. (!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  191. owner->getSelection()->ID,ID);
  192. }
  193. }
  194. else // Highlight
  195. {
  196. if(creature)
  197. owner->selectSlot(this);
  198. redraw();
  199. refr = true;
  200. }
  201. }
  202. }
  203. else //highlight or drop artifact
  204. {
  205. bool artSelected = false;
  206. if (CWindowWithArtifacts* chw = dynamic_cast<CWindowWithArtifacts*>(GH.topInt())) //dirty solution
  207. {
  208. const CArtifactsOfHero::SCommonPart *commonInfo = chw->artSets.front()->commonInfo;
  209. if (const CArtifactInstance *art = commonInfo->src.art)
  210. {
  211. const CGHeroInstance *srcHero = commonInfo->src.AOH->getHero();
  212. artSelected = true;
  213. ArtifactLocation src(srcHero, commonInfo->src.slotID);
  214. ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT);
  215. if (art->canBePutAt(dst, true))
  216. { //equip clicked stack
  217. if(dst.getArt())
  218. {
  219. //creature can wear only one active artifact
  220. //if we are placing a new one, the old one will be returned to the hero's backpack
  221. LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero, dst.getArt()->firstBackpackSlot(srcHero)));
  222. }
  223. LOCPLINT->cb->swapArtifacts(src, dst);
  224. }
  225. }
  226. }
  227. if (!artSelected && creature)
  228. {
  229. owner->selectSlot(this);
  230. if(creature)
  231. {
  232. for(auto & elem : owner->splitButtons)
  233. elem->block(false);
  234. }
  235. }
  236. redraw();
  237. refr = true;
  238. }
  239. if(refr) {hover(false); hover(true); } //to refresh statusbar
  240. }
  241. }
  242. void CGarrisonSlot::update()
  243. {
  244. if (getObj() != nullptr)
  245. {
  246. addUsedEvents(LCLICK | RCLICK | HOVER);
  247. myStack = getObj()->getStackPtr(ID);
  248. creature = myStack ? myStack->type : nullptr;
  249. }
  250. else
  251. {
  252. removeUsedEvents(LCLICK | RCLICK | HOVER);
  253. myStack = nullptr;
  254. creature = nullptr;
  255. }
  256. if (creature)
  257. {
  258. creatureImage->enable();
  259. creatureImage->setFrame(creature->iconIndex);
  260. stackCount->enable();
  261. stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
  262. }
  263. else
  264. {
  265. creatureImage->disable();
  266. stackCount->disable();
  267. }
  268. }
  269. CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, int Upg, const CStackInstance * Creature):
  270. ID(IID),
  271. owner(Owner),
  272. myStack(Creature),
  273. creature(Creature ? Creature->type : nullptr),
  274. upg(Upg)
  275. {
  276. OBJ_CONSTRUCTION_CAPTURING_ALL;
  277. if (getObj())
  278. addUsedEvents(LCLICK | RCLICK | HOVER);
  279. pos.x += x;
  280. pos.y += y;
  281. std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
  282. creatureImage = new CAnimImage(imgName, creature ? creature->iconIndex : 0);
  283. if (!creature)
  284. creatureImage->disable();
  285. selectionImage = new CAnimImage(imgName, 1);
  286. selectionImage->disable();
  287. if(Owner->smallIcons)
  288. {
  289. pos.w = 32;
  290. pos.h = 32;
  291. }
  292. else
  293. {
  294. pos.w = 58;
  295. pos.h = 64;
  296. }
  297. stackCount = new CLabel(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, BOTTOMRIGHT, Colors::WHITE);
  298. if (!creature)
  299. stackCount->disable();
  300. else
  301. stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
  302. }
  303. void CGarrisonInt::addSplitBtn(CAdventureMapButton * button)
  304. {
  305. addChild(button);
  306. button->recActions = defActions;
  307. splitButtons.push_back(button);
  308. button->block(getSelection() == nullptr);
  309. }
  310. void CGarrisonInt::createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSet * set, int posX, int posY, int distance, int Upg )
  311. {
  312. ret.resize(7);
  313. if (set)
  314. {
  315. for(auto & elem : set->Slots())
  316. {
  317. ret[elem.first.getNum()] = new CGarrisonSlot(this, posX + (elem.first.getNum()*distance), posY, elem.first, Upg, elem.second);
  318. }
  319. }
  320. for(int i=0; i<ret.size(); i++)
  321. if(!ret[i])
  322. ret[i] = new CGarrisonSlot(this, posX + (i*distance), posY, SlotID(i), Upg, nullptr);
  323. if (twoRows)
  324. for (int i=4; i<ret.size(); i++)
  325. {
  326. ret[i]->pos.x -= 126;
  327. ret[i]->pos.y += 37;
  328. };
  329. }
  330. void CGarrisonInt::createSlots()
  331. {
  332. OBJ_CONSTRUCTION_CAPTURING_ALL;
  333. int width = smallIcons? 32 : 58;
  334. createSet(slotsUp, armedObjs[0], 0, 0, width+interx, 0);
  335. createSet(slotsDown, armedObjs[1], garOffset.x, garOffset.y, width+interx, 1);
  336. }
  337. void CGarrisonInt::recreateSlots()
  338. {
  339. selectSlot(nullptr);
  340. setSplittingMode(false);
  341. for(auto & elem : splitButtons)
  342. elem->block(true);
  343. for(CGarrisonSlot * slot : slotsUp)
  344. slot->update();
  345. for(CGarrisonSlot * slot : slotsDown)
  346. slot->update();
  347. }
  348. void CGarrisonInt::splitClick()
  349. {
  350. if(!getSelection())
  351. return;
  352. setSplittingMode(!getSplittingMode());
  353. redraw();
  354. }
  355. void CGarrisonInt::splitStacks(int, int amountRight)
  356. {
  357. LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight);
  358. }
  359. CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point &garsOffset,
  360. SDL_Surface *pomsur, const Point& SurOffset,
  361. const CArmedInstance *s1, const CArmedInstance *s2,
  362. bool _removableUnits, bool smallImgs, bool _twoRows ) :
  363. highlighted(nullptr),
  364. inSplittingMode(false),
  365. interx(inx),
  366. garOffset(garsOffset),
  367. smallIcons(smallImgs),
  368. removableUnits(_removableUnits),
  369. twoRows(_twoRows)
  370. {
  371. setArmy(s1, false);
  372. setArmy(s2, true);
  373. pos.x += x;
  374. pos.y += y;
  375. createSlots();
  376. }
  377. const CGarrisonSlot * CGarrisonInt::getSelection()
  378. {
  379. return highlighted;
  380. }
  381. void CGarrisonInt::selectSlot(CGarrisonSlot *slot)
  382. {
  383. if (slot != highlighted)
  384. {
  385. if (highlighted)
  386. highlighted->setHighlight(false);
  387. highlighted = slot;
  388. for (auto button : splitButtons)
  389. button->block(highlighted == nullptr);
  390. if (highlighted)
  391. highlighted->setHighlight(true);
  392. }
  393. }
  394. void CGarrisonInt::setSplittingMode(bool on)
  395. {
  396. assert(on == false || highlighted != nullptr); //can't be in splitting mode without selection
  397. if (inSplittingMode || on)
  398. {
  399. for(CGarrisonSlot * slot : slotsUp)
  400. slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
  401. for(CGarrisonSlot * slot : slotsDown)
  402. slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
  403. inSplittingMode = on;
  404. }
  405. }
  406. bool CGarrisonInt::getSplittingMode()
  407. {
  408. return inSplittingMode;
  409. }
  410. void CGarrisonInt::setArmy(const CArmedInstance *army, bool bottomGarrison)
  411. {
  412. owned[bottomGarrison] = army ? (army->tempOwner == LOCPLINT->playerID || army->tempOwner == PlayerColor::UNFLAGGABLE) : false;
  413. armedObjs[bottomGarrison] = army;
  414. }
  415. CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits ):
  416. CWindowObject(PLAYER_COLORED, "GARRISON")
  417. {
  418. OBJ_CONSTRUCTION_CAPTURING_ALL;
  419. garr = new CGarrisonInt(92, 127, 4, Point(0,96), background->bg, Point(93,127), up, down, removableUnits);
  420. {
  421. CAdventureMapButton *split = new CAdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),88,314,"IDV6432.DEF");
  422. removeChild(split);
  423. garr->addSplitBtn(split);
  424. }
  425. quit = new CAdventureMapButton(CGI->generaltexth->tcommands[8],"",boost::bind(&CGarrisonWindow::close,this),399,314,"IOK6432.DEF",SDLK_RETURN);
  426. std::string titleText;
  427. if (garr->armedObjs[1]->tempOwner == garr->armedObjs[0]->tempOwner)
  428. titleText = CGI->generaltexth->allTexts[709];
  429. else
  430. {
  431. titleText = CGI->generaltexth->allTexts[35];
  432. boost::algorithm::replace_first(titleText, "%s", garr->armedObjs[0]->Slots().begin()->second->type->namePl);
  433. }
  434. new CLabel(275, 30, FONT_BIG, CENTER, Colors::YELLOW, titleText);
  435. new CAnimImage("CREST58", garr->armedObjs[0]->getOwner().getNum(), 0, 28, 124);
  436. new CAnimImage("PortraitsLarge", dynamic_cast<const CGHeroInstance*>(garr->armedObjs[1])->portrait, 0, 29, 222);
  437. }
  438. CGarrisonHolder::CGarrisonHolder()
  439. {
  440. }
  441. void CWindowWithGarrison::updateGarrisons()
  442. {
  443. garr->recreateSlots();
  444. }