CGarrisonInt.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. #include "StdInc.h"
  2. #include "CGarrisonInt.h"
  3. #include "CGuiHandler.h"
  4. #include "../CAnimation.h"
  5. #include "../CCreatureWindow.h"
  6. #include "../CGameInfo.h"
  7. #include "../CPlayerInterface.h"
  8. #include "../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(createCreWindow(myStack, CCreatureWindow::ARMY));
  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()> upgr = nullptr;
  130. std::function<void()> dism = nullptr;
  131. if(canUpgrade) upgr = [=] { LOCPLINT->cb->upgradeCreature(getObj(), ID, pom.newID[0]); };
  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. CIntObject *creWindow = createCreWindow(myStack, CCreatureWindow::HERO, upgr, dism, &pom);
  140. GH.pushInt(creWindow);
  141. }
  142. else
  143. {
  144. // Only allow certain moves if troops aren't removable or not ours.
  145. if ( ( owner->getSelection()->our()//our creature is selected
  146. || owner->getSelection()->creature == creature )//or we are rebalancing army
  147. && ( owner->removableUnits
  148. || (upg == 0 && ( owner->getSelection()->upg == 1 && !creature ) )
  149. || (upg == 1 && owner->getSelection()->upg == 1 ) ) )
  150. {
  151. //we want to split
  152. if((owner->getSplittingMode() || LOCPLINT->shiftPressed())
  153. && (!creature
  154. || (creature == owner->getSelection()->creature)))
  155. {
  156. owner->p2 = ID; //store the second stack pos
  157. owner->pb = upg;//store the second stack owner (up or down army)
  158. owner->setSplittingMode(false);
  159. int minLeft=0, minRight=0;
  160. if(upg != owner->getSelection()->upg) //not splitting within same army
  161. {
  162. if(owner->getSelection()->getObj()->stacksCount() == 1 //we're splitting away the last stack
  163. && owner->getSelection()->getObj()->needsLastStack() )
  164. {
  165. minLeft = 1;
  166. }
  167. if(getObj()->stacksCount() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
  168. && owner->getSelection()->creature == creature
  169. && getObj()->needsLastStack() )
  170. {
  171. minRight = 1;
  172. }
  173. }
  174. int countLeft = owner->getSelection()->myStack ? owner->getSelection()->myStack->count : 0;
  175. int countRight = myStack ? myStack->count : 0;
  176. GH.pushInt(new CSplitWindow(owner->getSelection()->creature, boost::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
  177. minLeft, minRight, countLeft, countRight));
  178. refr = true;
  179. }
  180. else if(creature != owner->getSelection()->creature) //swap
  181. {
  182. LOCPLINT->cb->swapCreatures(
  183. (!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  184. (!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  185. ID,owner->getSelection()->ID);
  186. }
  187. else //merge
  188. {
  189. LOCPLINT->cb->mergeStacks(
  190. (!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  191. (!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
  192. owner->getSelection()->ID,ID);
  193. }
  194. }
  195. else // Highlight
  196. {
  197. if(creature)
  198. owner->selectSlot(this);
  199. redraw();
  200. refr = true;
  201. }
  202. }
  203. }
  204. else //highlight or drop artifact
  205. {
  206. bool artSelected = false;
  207. if (CWindowWithArtifacts* chw = dynamic_cast<CWindowWithArtifacts*>(GH.topInt())) //dirty solution
  208. {
  209. const CArtifactsOfHero::SCommonPart *commonInfo = chw->artSets.front()->commonInfo;
  210. if (const CArtifactInstance *art = commonInfo->src.art)
  211. {
  212. const CGHeroInstance *srcHero = commonInfo->src.AOH->getHero();
  213. artSelected = true;
  214. ArtifactLocation src(srcHero, commonInfo->src.slotID);
  215. ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT);
  216. if (art->canBePutAt(dst, true))
  217. { //equip clicked stack
  218. if(dst.getArt())
  219. {
  220. //creature can wear only one active artifact
  221. //if we are placing a new one, the old one will be returned to the hero's backpack
  222. LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero, dst.getArt()->firstBackpackSlot(srcHero)));
  223. }
  224. LOCPLINT->cb->swapArtifacts(src, dst);
  225. }
  226. }
  227. }
  228. if (!artSelected && creature)
  229. {
  230. owner->selectSlot(this);
  231. if(creature)
  232. {
  233. for(auto & elem : owner->splitButtons)
  234. elem->block(false);
  235. }
  236. }
  237. redraw();
  238. refr = true;
  239. }
  240. if(refr) {hover(false); hover(true); } //to refresh statusbar
  241. }
  242. }
  243. void CGarrisonSlot::update()
  244. {
  245. if (getObj() != nullptr)
  246. {
  247. addUsedEvents(LCLICK | RCLICK | HOVER);
  248. myStack = getObj()->getStackPtr(ID);
  249. creature = myStack ? myStack->type : nullptr;
  250. }
  251. else
  252. {
  253. removeUsedEvents(LCLICK | RCLICK | HOVER);
  254. myStack = nullptr;
  255. creature = nullptr;
  256. }
  257. if (creature)
  258. {
  259. creatureImage->enable();
  260. creatureImage->setFrame(creature->iconIndex);
  261. stackCount->enable();
  262. stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
  263. }
  264. else
  265. {
  266. creatureImage->disable();
  267. stackCount->disable();
  268. }
  269. }
  270. CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, int Upg, const CStackInstance * Creature):
  271. ID(IID),
  272. owner(Owner),
  273. myStack(Creature),
  274. creature(Creature ? Creature->type : nullptr),
  275. upg(Upg)
  276. {
  277. OBJ_CONSTRUCTION_CAPTURING_ALL;
  278. if (getObj())
  279. addUsedEvents(LCLICK | RCLICK | HOVER);
  280. pos.x += x;
  281. pos.y += y;
  282. std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
  283. creatureImage = new CAnimImage(imgName, creature ? creature->iconIndex : 0);
  284. if (!creature)
  285. creatureImage->disable();
  286. selectionImage = new CAnimImage(imgName, 1);
  287. selectionImage->disable();
  288. if(Owner->smallIcons)
  289. {
  290. pos.w = 32;
  291. pos.h = 32;
  292. }
  293. else
  294. {
  295. pos.w = 58;
  296. pos.h = 64;
  297. }
  298. stackCount = new CLabel(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, BOTTOMRIGHT, Colors::WHITE);
  299. if (!creature)
  300. stackCount->disable();
  301. else
  302. stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
  303. }
  304. void CGarrisonInt::addSplitBtn(CAdventureMapButton * button)
  305. {
  306. addChild(button);
  307. button->recActions = defActions;
  308. splitButtons.push_back(button);
  309. button->block(getSelection() == nullptr);
  310. }
  311. void CGarrisonInt::createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSet * set, int posX, int posY, int distance, int Upg )
  312. {
  313. ret.resize(7);
  314. if (set)
  315. {
  316. for(auto & elem : set->Slots())
  317. {
  318. ret[elem.first.getNum()] = new CGarrisonSlot(this, posX + (elem.first.getNum()*distance), posY, elem.first, Upg, elem.second);
  319. }
  320. }
  321. for(int i=0; i<ret.size(); i++)
  322. if(!ret[i])
  323. ret[i] = new CGarrisonSlot(this, posX + (i*distance), posY, SlotID(i), Upg, nullptr);
  324. if (twoRows)
  325. for (int i=4; i<ret.size(); i++)
  326. {
  327. ret[i]->pos.x -= 126;
  328. ret[i]->pos.y += 37;
  329. };
  330. }
  331. void CGarrisonInt::createSlots()
  332. {
  333. OBJ_CONSTRUCTION_CAPTURING_ALL;
  334. int width = smallIcons? 32 : 58;
  335. createSet(slotsUp, armedObjs[0], 0, 0, width+interx, 0);
  336. createSet(slotsDown, armedObjs[1], garOffset.x, garOffset.y, width+interx, 1);
  337. }
  338. void CGarrisonInt::recreateSlots()
  339. {
  340. selectSlot(nullptr);
  341. setSplittingMode(false);
  342. for(auto & elem : splitButtons)
  343. elem->block(true);
  344. for(CGarrisonSlot * slot : slotsUp)
  345. slot->update();
  346. for(CGarrisonSlot * slot : slotsDown)
  347. slot->update();
  348. }
  349. void CGarrisonInt::splitClick()
  350. {
  351. if(!getSelection())
  352. return;
  353. setSplittingMode(!getSplittingMode());
  354. redraw();
  355. }
  356. void CGarrisonInt::splitStacks(int, int amountRight)
  357. {
  358. LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight);
  359. }
  360. CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point &garsOffset,
  361. SDL_Surface *pomsur, const Point& SurOffset,
  362. const CArmedInstance *s1, const CArmedInstance *s2,
  363. bool _removableUnits, bool smallImgs, bool _twoRows ) :
  364. highlighted(nullptr),
  365. inSplittingMode(false),
  366. interx(inx),
  367. garOffset(garsOffset),
  368. smallIcons(smallImgs),
  369. removableUnits(_removableUnits),
  370. twoRows(_twoRows)
  371. {
  372. setArmy(s1, false);
  373. setArmy(s2, true);
  374. pos.x += x;
  375. pos.y += y;
  376. createSlots();
  377. }
  378. const CGarrisonSlot * CGarrisonInt::getSelection()
  379. {
  380. return highlighted;
  381. }
  382. void CGarrisonInt::selectSlot(CGarrisonSlot *slot)
  383. {
  384. if (slot != highlighted)
  385. {
  386. if (highlighted)
  387. highlighted->setHighlight(false);
  388. highlighted = slot;
  389. for (auto button : splitButtons)
  390. button->block(highlighted == nullptr);
  391. if (highlighted)
  392. highlighted->setHighlight(true);
  393. }
  394. }
  395. void CGarrisonInt::setSplittingMode(bool on)
  396. {
  397. assert(on == false || highlighted != nullptr); //can't be in splitting mode without selection
  398. if (inSplittingMode || on)
  399. {
  400. for(CGarrisonSlot * slot : slotsUp)
  401. slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
  402. for(CGarrisonSlot * slot : slotsDown)
  403. slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
  404. inSplittingMode = on;
  405. }
  406. }
  407. bool CGarrisonInt::getSplittingMode()
  408. {
  409. return inSplittingMode;
  410. }
  411. void CGarrisonInt::setArmy(const CArmedInstance *army, bool bottomGarrison)
  412. {
  413. owned[bottomGarrison] = army ? (army->tempOwner == LOCPLINT->playerID || army->tempOwner == PlayerColor::UNFLAGGABLE) : false;
  414. armedObjs[bottomGarrison] = army;
  415. }
  416. CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits ):
  417. CWindowObject(PLAYER_COLORED, "GARRISON")
  418. {
  419. OBJ_CONSTRUCTION_CAPTURING_ALL;
  420. garr = new CGarrisonInt(92, 127, 4, Point(0,96), background->bg, Point(93,127), up, down, removableUnits);
  421. {
  422. CAdventureMapButton *split = new CAdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),88,314,"IDV6432.DEF");
  423. removeChild(split);
  424. garr->addSplitBtn(split);
  425. }
  426. quit = new CAdventureMapButton(CGI->generaltexth->tcommands[8],"",boost::bind(&CGarrisonWindow::close,this),399,314,"IOK6432.DEF",SDLK_RETURN);
  427. std::string titleText;
  428. if (garr->armedObjs[1]->tempOwner == garr->armedObjs[0]->tempOwner)
  429. titleText = CGI->generaltexth->allTexts[709];
  430. else
  431. {
  432. titleText = CGI->generaltexth->allTexts[35];
  433. boost::algorithm::replace_first(titleText, "%s", garr->armedObjs[0]->Slots().begin()->second->type->namePl);
  434. }
  435. new CLabel(275, 30, FONT_BIG, CENTER, Colors::YELLOW, titleText);
  436. new CAnimImage("CREST58", garr->armedObjs[0]->getOwner().getNum(), 0, 28, 124);
  437. new CAnimImage("PortraitsLarge", dynamic_cast<const CGHeroInstance*>(garr->armedObjs[1])->portrait, 0, 29, 222);
  438. }
  439. CGarrisonHolder::CGarrisonHolder()
  440. {
  441. }
  442. void CWindowWithGarrison::updateGarrisons()
  443. {
  444. garr->recreateSlots();
  445. }