BattleFieldController.cpp 20 KB

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