CBattleFieldController.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*
  2. * CBattleFieldController.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 "CBattleFieldController.h"
  12. #include "CBattleInterface.h"
  13. #include "CBattleActionsController.h"
  14. #include "CBattleInterfaceClasses.h"
  15. #include "CBattleSiegeController.h"
  16. #include "CBattleStacksController.h"
  17. #include "CBattleObstacleController.h"
  18. #include "../CGameInfo.h"
  19. #include "../../CCallback.h"
  20. #include "../gui/CAnimation.h"
  21. #include "../gui/CCanvas.h"
  22. #include "../gui/CGuiHandler.h"
  23. #include "../CPlayerInterface.h"
  24. #include "../gui/CCursorHandler.h"
  25. #include "../../lib/BattleFieldHandler.h"
  26. #include "../../lib/CConfigHandler.h"
  27. #include "../../lib/CStack.h"
  28. #include "../../lib/spells/ISpellMechanics.h"
  29. CBattleFieldController::CBattleFieldController(CBattleInterface * owner):
  30. owner(owner),
  31. attackingHex(-1)
  32. {
  33. OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
  34. pos.w = owner->pos.w;
  35. pos.h = owner->pos.h;
  36. //preparing cells and hexes
  37. cellBorder = IImage::createFromFile("CCELLGRD.BMP");
  38. cellShade = IImage::createFromFile("CCELLSHD.BMP");
  39. if(!owner->siegeController)
  40. {
  41. auto bfieldType = owner->curInt->cb->battleGetBattlefieldType();
  42. if(bfieldType == BattleField::NONE)
  43. logGlobal->error("Invalid battlefield returned for current battle");
  44. else
  45. background = IImage::createFromFile(bfieldType.getInfo()->graphics);
  46. }
  47. else
  48. {
  49. std::string backgroundName = owner->siegeController->getBattleBackgroundName();
  50. background = IImage::createFromFile(backgroundName);
  51. }
  52. //preparing graphic with cell borders
  53. cellBorders = std::make_shared<CCanvas>(Point(background->width(), background->height()));
  54. for (int i=0; i<GameConstants::BFIELD_SIZE; ++i)
  55. cellBorders->draw(cellBorder, hexPositionLocal(i).topLeft());
  56. backgroundWithHexes = std::make_shared<CCanvas>(Point(background->width(), background->height()));
  57. for (int h = 0; h < GameConstants::BFIELD_SIZE; ++h)
  58. {
  59. auto hex = std::make_shared<CClickableHex>();
  60. hex->myNumber = h;
  61. hex->pos = hexPosition(h);
  62. hex->myInterface = owner;
  63. bfield.push_back(hex);
  64. }
  65. }
  66. void CBattleFieldController::showBackgroundImage(SDL_Surface *to)
  67. {
  68. background->draw(to, owner->pos.x, owner->pos.y);
  69. if (settings["battle"]["cellBorders"].Bool())
  70. cellBorders->copyTo(to, owner->pos.topLeft());
  71. }
  72. void CBattleFieldController::showBackgroundImageWithHexes(SDL_Surface *to)
  73. {
  74. backgroundWithHexes->copyTo(to, owner->pos.topLeft());
  75. }
  76. void CBattleFieldController::redrawBackgroundWithHexes()
  77. {
  78. const CStack *activeStack = owner->stacksController->getActiveStack();
  79. std::vector<BattleHex> attackableHexes;
  80. if (activeStack)
  81. occupyableHexes = owner->curInt->cb->battleGetAvailableHexes(activeStack, true, &attackableHexes);
  82. auto accessibility = owner->curInt->cb->getAccesibility();
  83. for(int i = 0; i < accessibility.size(); i++)
  84. stackCountOutsideHexes[i] = (accessibility[i] == EAccessibility::ACCESSIBLE);
  85. //prepare background graphic with hexes and shaded hexes
  86. backgroundWithHexes->draw(background, Point(0,0));
  87. owner->obstacleController->redrawBackgroundWithHexes(backgroundWithHexes);
  88. if (settings["battle"]["stackRange"].Bool())
  89. {
  90. std::vector<BattleHex> hexesToShade = occupyableHexes;
  91. hexesToShade.insert(hexesToShade.end(), attackableHexes.begin(), attackableHexes.end());
  92. for (BattleHex hex : hexesToShade)
  93. {
  94. backgroundWithHexes->draw(cellShade, hexPositionLocal(hex).topLeft());
  95. }
  96. }
  97. if(settings["battle"]["cellBorders"].Bool())
  98. backgroundWithHexes->draw(cellBorders, Point(0, 0));
  99. }
  100. void CBattleFieldController::showHighlightedHex(std::shared_ptr<CCanvas> to, BattleHex hex, bool darkBorder)
  101. {
  102. Point hexPos = hexPosition(hex).topLeft();
  103. to->draw(cellShade, hexPos);
  104. if(!darkBorder && settings["battle"]["cellBorders"].Bool())
  105. to->draw(cellBorder, hexPos);
  106. }
  107. std::set<BattleHex> CBattleFieldController::getHighlightedHexesStackRange()
  108. {
  109. std::set<BattleHex> result;
  110. if ( !owner->stacksController->getActiveStack())
  111. return result;
  112. if ( !settings["battle"]["stackRange"].Bool())
  113. return result;
  114. auto hoveredHex = getHoveredHex();
  115. std::set<BattleHex> set = owner->curInt->cb->battleGetAttackedHexes(owner->stacksController->getActiveStack(), hoveredHex, attackingHex);
  116. for(BattleHex hex : set)
  117. result.insert(hex);
  118. // display the movement shadow of the stack at b (i.e. stack under mouse)
  119. const CStack * const shere = owner->curInt->cb->battleGetStackByPos(hoveredHex, false);
  120. if(shere && shere != owner->stacksController->getActiveStack() && shere->alive())
  121. {
  122. std::vector<BattleHex> v = owner->curInt->cb->battleGetAvailableHexes(shere, true, nullptr);
  123. for(BattleHex hex : v)
  124. result.insert(hex);
  125. }
  126. return result;
  127. }
  128. std::set<BattleHex> CBattleFieldController::getHighlightedHexesSpellRange()
  129. {
  130. std::set<BattleHex> result;
  131. auto hoveredHex = getHoveredHex();
  132. if(!settings["battle"]["mouseShadow"].Bool())
  133. return result;
  134. const spells::Caster *caster = nullptr;
  135. const CSpell *spell = nullptr;
  136. spells::Mode mode = spells::Mode::HERO;
  137. if(owner->actionsController->spellcastingModeActive())//hero casts spell
  138. {
  139. spell = owner->actionsController->selectedSpell().toSpell();
  140. caster = owner->getActiveHero();
  141. }
  142. else if(owner->stacksController->activeStackSpellToCast() != SpellID::NONE)//stack casts spell
  143. {
  144. spell = SpellID(owner->stacksController->activeStackSpellToCast()).toSpell();
  145. caster = owner->stacksController->getActiveStack();
  146. mode = spells::Mode::CREATURE_ACTIVE;
  147. }
  148. if(caster && spell) //when casting spell
  149. {
  150. // printing shaded hex(es)
  151. spells::BattleCast event(owner->curInt->cb.get(), caster, mode, spell);
  152. auto shaded = spell->battleMechanics(&event)->rangeInHexes(hoveredHex);
  153. for(BattleHex shadedHex : shaded)
  154. {
  155. if((shadedHex.getX() != 0) && (shadedHex.getX() != GameConstants::BFIELD_WIDTH - 1))
  156. result.insert(shadedHex);
  157. }
  158. }
  159. else if(owner->active) //always highlight pointed hex
  160. {
  161. if(hoveredHex.getX() != 0 && hoveredHex.getX() != GameConstants::BFIELD_WIDTH - 1)
  162. result.insert(hoveredHex);
  163. }
  164. return result;
  165. }
  166. void CBattleFieldController::showHighlightedHexes(SDL_Surface *to)
  167. {
  168. auto canvas = std::make_shared<CCanvas>(to);
  169. std::set<BattleHex> hoveredStack = getHighlightedHexesStackRange();
  170. std::set<BattleHex> hoveredMouse = getHighlightedHexesSpellRange();
  171. for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
  172. {
  173. bool stack = hoveredStack.count(b);
  174. bool mouse = hoveredMouse.count(b);
  175. if ( stack && mouse )
  176. {
  177. // area where enemy stack can move AND affected by mouse cursor - create darker highlight by blitting twice
  178. showHighlightedHex(canvas, b, true);
  179. showHighlightedHex(canvas, b, true);
  180. }
  181. if ( !stack && mouse )
  182. {
  183. showHighlightedHex(canvas, b, true);
  184. }
  185. if ( stack && !mouse )
  186. {
  187. showHighlightedHex(canvas, b, false);
  188. }
  189. }
  190. }
  191. Rect CBattleFieldController::hexPositionLocal(BattleHex hex) const
  192. {
  193. int x = 14 + ((hex.getY())%2==0 ? 22 : 0) + 44*hex.getX();
  194. int y = 86 + 42 *hex.getY();
  195. int w = cellShade->width();
  196. int h = cellShade->height();
  197. return Rect(x, y, w, h);
  198. }
  199. Rect CBattleFieldController::hexPosition(BattleHex hex) const
  200. {
  201. return hexPositionLocal(hex) + owner->pos.topLeft();
  202. }
  203. bool CBattleFieldController::isPixelInHex(Point const & position)
  204. {
  205. return !cellShade->isTransparent(position);
  206. }
  207. BattleHex CBattleFieldController::getHoveredHex()
  208. {
  209. for ( auto const & hex : bfield)
  210. if (hex->hovered && hex->strictHovered)
  211. return hex->myNumber;
  212. return BattleHex::INVALID;
  213. }
  214. void CBattleFieldController::setBattleCursor(BattleHex myNumber)
  215. {
  216. Rect hoveredHexPos = hexPosition(myNumber);
  217. CCursorHandler *cursor = CCS->curh;
  218. const double subdividingAngle = 2.0*M_PI/6.0; // Divide a hex into six sectors.
  219. const double hexMidX = hoveredHexPos.x + hoveredHexPos.w/2.0;
  220. const double hexMidY = hoveredHexPos.y + hoveredHexPos.h/2.0;
  221. const double cursorHexAngle = M_PI - atan2(hexMidY - cursor->ypos, cursor->xpos - hexMidX) + subdividingAngle/2; //TODO: refactor this nightmare
  222. const double sector = fmod(cursorHexAngle/subdividingAngle, 6.0);
  223. const int zigzagCorrection = !((myNumber/GameConstants::BFIELD_WIDTH)%2); // Off-by-one correction needed to deal with the odd battlefield rows.
  224. std::vector<int> sectorCursor; // From left to bottom left.
  225. sectorCursor.push_back(8);
  226. sectorCursor.push_back(9);
  227. sectorCursor.push_back(10);
  228. sectorCursor.push_back(11);
  229. sectorCursor.push_back(12);
  230. sectorCursor.push_back(7);
  231. const bool doubleWide = owner->stacksController->getActiveStack()->doubleWide();
  232. bool aboveAttackable = true, belowAttackable = true;
  233. // Exclude directions which cannot be attacked from.
  234. // Check to the left.
  235. if (myNumber%GameConstants::BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - 1))
  236. {
  237. sectorCursor[0] = -1;
  238. }
  239. // Check top left, top right as well as above for 2-hex creatures.
  240. if (myNumber/GameConstants::BFIELD_WIDTH == 0)
  241. {
  242. sectorCursor[1] = -1;
  243. sectorCursor[2] = -1;
  244. aboveAttackable = false;
  245. }
  246. else
  247. {
  248. if (doubleWide)
  249. {
  250. bool attackRow[4] = {true, true, true, true};
  251. if (myNumber%GameConstants::BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH - 2 + zigzagCorrection))
  252. attackRow[0] = false;
  253. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  254. attackRow[1] = false;
  255. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH + zigzagCorrection))
  256. attackRow[2] = false;
  257. if (myNumber%GameConstants::BFIELD_WIDTH >= GameConstants::BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH + 1 + zigzagCorrection))
  258. attackRow[3] = false;
  259. if (!(attackRow[0] && attackRow[1]))
  260. sectorCursor[1] = -1;
  261. if (!(attackRow[1] && attackRow[2]))
  262. aboveAttackable = false;
  263. if (!(attackRow[2] && attackRow[3]))
  264. sectorCursor[2] = -1;
  265. }
  266. else
  267. {
  268. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  269. sectorCursor[1] = -1;
  270. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH + zigzagCorrection))
  271. sectorCursor[2] = -1;
  272. }
  273. }
  274. // Check to the right.
  275. if (myNumber%GameConstants::BFIELD_WIDTH >= GameConstants::BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + 1))
  276. {
  277. sectorCursor[3] = -1;
  278. }
  279. // Check bottom right, bottom left as well as below for 2-hex creatures.
  280. if (myNumber/GameConstants::BFIELD_WIDTH == GameConstants::BFIELD_HEIGHT - 1)
  281. {
  282. sectorCursor[4] = -1;
  283. sectorCursor[5] = -1;
  284. belowAttackable = false;
  285. }
  286. else
  287. {
  288. if (doubleWide)
  289. {
  290. bool attackRow[4] = {true, true, true, true};
  291. if (myNumber%GameConstants::BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH - 2 + zigzagCorrection))
  292. attackRow[0] = false;
  293. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  294. attackRow[1] = false;
  295. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH + zigzagCorrection))
  296. attackRow[2] = false;
  297. if (myNumber%GameConstants::BFIELD_WIDTH >= GameConstants::BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH + 1 + zigzagCorrection))
  298. attackRow[3] = false;
  299. if (!(attackRow[0] && attackRow[1]))
  300. sectorCursor[5] = -1;
  301. if (!(attackRow[1] && attackRow[2]))
  302. belowAttackable = false;
  303. if (!(attackRow[2] && attackRow[3]))
  304. sectorCursor[4] = -1;
  305. }
  306. else
  307. {
  308. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH + zigzagCorrection))
  309. sectorCursor[4] = -1;
  310. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  311. sectorCursor[5] = -1;
  312. }
  313. }
  314. // Determine index from sector.
  315. int cursorIndex;
  316. if (doubleWide)
  317. {
  318. sectorCursor.insert(sectorCursor.begin() + 5, belowAttackable ? 13 : -1);
  319. sectorCursor.insert(sectorCursor.begin() + 2, aboveAttackable ? 14 : -1);
  320. if (sector < 1.5)
  321. cursorIndex = static_cast<int>(sector);
  322. else if (sector >= 1.5 && sector < 2.5)
  323. cursorIndex = 2;
  324. else if (sector >= 2.5 && sector < 4.5)
  325. cursorIndex = (int) sector + 1;
  326. else if (sector >= 4.5 && sector < 5.5)
  327. cursorIndex = 6;
  328. else
  329. cursorIndex = (int) sector + 2;
  330. }
  331. else
  332. {
  333. cursorIndex = static_cast<int>(sector);
  334. }
  335. // Generally should NEVER happen, but to avoid the possibility of having endless loop below... [#1016]
  336. if (!vstd::contains_if (sectorCursor, [](int sc) { return sc != -1; }))
  337. {
  338. logGlobal->error("Error: for hex %d cannot find a hex to attack from!", myNumber);
  339. attackingHex = -1;
  340. return;
  341. }
  342. // Find the closest direction attackable, starting with the right one.
  343. // FIXME: Is this really how the original H3 client does it?
  344. int i = 0;
  345. while (sectorCursor[(cursorIndex + i)%sectorCursor.size()] == -1) //Why hast thou forsaken me?
  346. i = i <= 0 ? 1 - i : -i; // 0, 1, -1, 2, -2, 3, -3 etc..
  347. int index = (cursorIndex + i)%sectorCursor.size(); //hopefully we get elements from sectorCursor
  348. cursor->changeGraphic(ECursor::COMBAT, sectorCursor[index]);
  349. switch (index)
  350. {
  351. case 0:
  352. attackingHex = myNumber - 1; //left
  353. break;
  354. case 1:
  355. attackingHex = myNumber - GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection; //top left
  356. break;
  357. case 2:
  358. attackingHex = myNumber - GameConstants::BFIELD_WIDTH + zigzagCorrection; //top right
  359. break;
  360. case 3:
  361. attackingHex = myNumber + 1; //right
  362. break;
  363. case 4:
  364. attackingHex = myNumber + GameConstants::BFIELD_WIDTH + zigzagCorrection; //bottom right
  365. break;
  366. case 5:
  367. attackingHex = myNumber + GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection; //bottom left
  368. break;
  369. }
  370. BattleHex hex(attackingHex);
  371. if (!hex.isValid())
  372. attackingHex = -1;
  373. }
  374. BattleHex CBattleFieldController::fromWhichHexAttack(BattleHex myNumber)
  375. {
  376. //TODO far too much repeating code
  377. BattleHex destHex;
  378. switch(CCS->curh->frame)
  379. {
  380. case 12: //from bottom right
  381. {
  382. bool doubleWide = owner->stacksController->getActiveStack()->doubleWide();
  383. destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ) +
  384. (owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
  385. if(vstd::contains(occupyableHexes, destHex))
  386. return destHex;
  387. else if(owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  388. {
  389. if (vstd::contains(occupyableHexes, destHex+1))
  390. return destHex+1;
  391. }
  392. else //if we are defender
  393. {
  394. if(vstd::contains(occupyableHexes, destHex-1))
  395. return destHex-1;
  396. }
  397. break;
  398. }
  399. case 7: //from bottom left
  400. {
  401. destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH-1 : GameConstants::BFIELD_WIDTH );
  402. if (vstd::contains(occupyableHexes, destHex))
  403. return destHex;
  404. else if(owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  405. {
  406. if(vstd::contains(occupyableHexes, destHex+1))
  407. return destHex+1;
  408. }
  409. else //we are defender
  410. {
  411. if(vstd::contains(occupyableHexes, destHex-1))
  412. return destHex-1;
  413. }
  414. break;
  415. }
  416. case 8: //from left
  417. {
  418. if(owner->stacksController->getActiveStack()->doubleWide() && owner->stacksController->getActiveStack()->side == BattleSide::DEFENDER)
  419. {
  420. std::vector<BattleHex> acc = owner->curInt->cb->battleGetAvailableHexes(owner->stacksController->getActiveStack());
  421. if (vstd::contains(acc, myNumber))
  422. return myNumber - 1;
  423. else
  424. return myNumber - 2;
  425. }
  426. else
  427. {
  428. return myNumber - 1;
  429. }
  430. break;
  431. }
  432. case 9: //from top left
  433. {
  434. destHex = myNumber - ((myNumber/GameConstants::BFIELD_WIDTH) % 2 ? GameConstants::BFIELD_WIDTH + 1 : GameConstants::BFIELD_WIDTH);
  435. if(vstd::contains(occupyableHexes, destHex))
  436. return destHex;
  437. else if(owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  438. {
  439. if(vstd::contains(occupyableHexes, destHex+1))
  440. return destHex+1;
  441. }
  442. else //if we are defender
  443. {
  444. if(vstd::contains(occupyableHexes, destHex-1))
  445. return destHex-1;
  446. }
  447. break;
  448. }
  449. case 10: //from top right
  450. {
  451. bool doubleWide = owner->stacksController->getActiveStack()->doubleWide();
  452. destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ) +
  453. (owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
  454. if(vstd::contains(occupyableHexes, destHex))
  455. return destHex;
  456. else if(owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  457. {
  458. if(vstd::contains(occupyableHexes, destHex+1))
  459. return destHex+1;
  460. }
  461. else //if we are defender
  462. {
  463. if(vstd::contains(occupyableHexes, destHex-1))
  464. return destHex-1;
  465. }
  466. break;
  467. }
  468. case 11: //from right
  469. {
  470. if(owner->stacksController->getActiveStack()->doubleWide() && owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  471. {
  472. std::vector<BattleHex> acc = owner->curInt->cb->battleGetAvailableHexes(owner->stacksController->getActiveStack());
  473. if(vstd::contains(acc, myNumber))
  474. return myNumber + 1;
  475. else
  476. return myNumber + 2;
  477. }
  478. else
  479. {
  480. return myNumber + 1;
  481. }
  482. break;
  483. }
  484. case 13: //from bottom
  485. {
  486. destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 );
  487. if(vstd::contains(occupyableHexes, destHex))
  488. return destHex;
  489. else if(owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  490. {
  491. if(vstd::contains(occupyableHexes, destHex+1))
  492. return destHex+1;
  493. }
  494. else //if we are defender
  495. {
  496. if(vstd::contains(occupyableHexes, destHex-1))
  497. return destHex-1;
  498. }
  499. break;
  500. }
  501. case 14: //from top
  502. {
  503. destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 );
  504. if (vstd::contains(occupyableHexes, destHex))
  505. return destHex;
  506. else if(owner->stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  507. {
  508. if(vstd::contains(occupyableHexes, destHex+1))
  509. return destHex+1;
  510. }
  511. else //if we are defender
  512. {
  513. if(vstd::contains(occupyableHexes, destHex-1))
  514. return destHex-1;
  515. }
  516. break;
  517. }
  518. }
  519. return -1;
  520. }
  521. bool CBattleFieldController::isTileAttackable(const BattleHex & number) const
  522. {
  523. for (auto & elem : occupyableHexes)
  524. {
  525. if (BattleHex::mutualPosition(elem, number) != -1 || elem == number)
  526. return true;
  527. }
  528. return false;
  529. }
  530. bool CBattleFieldController::stackCountOutsideHex(const BattleHex & number) const
  531. {
  532. return stackCountOutsideHexes[number];
  533. }