BattleFieldController.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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. return result;
  192. }
  193. std::set<BattleHex> BattleFieldController::getHighlightedHexesMovementTarget()
  194. {
  195. const CStack * stack = owner.stacksController->getActiveStack();
  196. std::set<BattleHex> result;
  197. auto hoveredHex = getHoveredHex();
  198. if (stack)
  199. {
  200. std::vector<BattleHex> v = owner.curInt->cb->battleGetAvailableHexes(stack, false, nullptr);
  201. if (vstd::contains(v,hoveredHex))
  202. {
  203. result.insert(hoveredHex);
  204. if (stack->doubleWide())
  205. result.insert(stack->occupiedHex(hoveredHex));
  206. }
  207. else
  208. {
  209. if (stack->doubleWide())
  210. {
  211. for (auto const & hex : v)
  212. {
  213. if (stack->occupiedHex(hex) == hoveredHex)
  214. {
  215. result.insert(hoveredHex);
  216. result.insert(hex);
  217. }
  218. }
  219. }
  220. }
  221. }
  222. return result;
  223. }
  224. void BattleFieldController::showHighlightedHexes(Canvas & canvas)
  225. {
  226. std::set<BattleHex> hoveredStack = getHighlightedHexesStackRange();
  227. std::set<BattleHex> hoveredSpell = getHighlightedHexesSpellRange();
  228. std::set<BattleHex> hoveredMove = getHighlightedHexesMovementTarget();
  229. auto const & hoveredMouse = owner.actionsController->spellcastingModeActive() ? hoveredSpell : hoveredMove;
  230. for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
  231. {
  232. bool stack = hoveredStack.count(b);
  233. bool mouse = hoveredMouse.count(b);
  234. if ( stack && mouse )
  235. {
  236. // area where enemy stack can move AND affected by mouse cursor - create darker highlight by blitting twice
  237. showHighlightedHex(canvas, b, true);
  238. showHighlightedHex(canvas, b, true);
  239. }
  240. if ( !stack && mouse )
  241. {
  242. showHighlightedHex(canvas, b, true);
  243. }
  244. if ( stack && !mouse )
  245. {
  246. showHighlightedHex(canvas, b, false);
  247. }
  248. }
  249. }
  250. Rect BattleFieldController::hexPositionLocal(BattleHex hex) const
  251. {
  252. int x = 14 + ((hex.getY())%2==0 ? 22 : 0) + 44*hex.getX();
  253. int y = 86 + 42 *hex.getY();
  254. int w = cellShade->width();
  255. int h = cellShade->height();
  256. return Rect(x, y, w, h);
  257. }
  258. Rect BattleFieldController::hexPositionAbsolute(BattleHex hex) const
  259. {
  260. return hexPositionLocal(hex) + owner.pos.topLeft();
  261. }
  262. bool BattleFieldController::isPixelInHex(Point const & position)
  263. {
  264. return !cellShade->isTransparent(position);
  265. }
  266. BattleHex BattleFieldController::getHoveredHex()
  267. {
  268. for ( auto const & hex : bfield)
  269. if (hex->hovered && hex->strictHovered)
  270. return hex->myNumber;
  271. return BattleHex::INVALID;
  272. }
  273. void BattleFieldController::setBattleCursor(BattleHex myNumber)
  274. {
  275. Rect hoveredHexPos = hexPositionAbsolute(myNumber);
  276. CCursorHandler *cursor = CCS->curh;
  277. const double subdividingAngle = 2.0*M_PI/6.0; // Divide a hex into six sectors.
  278. const double hexMidX = hoveredHexPos.x + hoveredHexPos.w/2.0;
  279. const double hexMidY = hoveredHexPos.y + hoveredHexPos.h/2.0;
  280. const double cursorHexAngle = M_PI - atan2(hexMidY - cursor->ypos, cursor->xpos - hexMidX) + subdividingAngle/2; //TODO: refactor this nightmare
  281. const double sector = fmod(cursorHexAngle/subdividingAngle, 6.0);
  282. const int zigzagCorrection = !((myNumber/GameConstants::BFIELD_WIDTH)%2); // Off-by-one correction needed to deal with the odd battlefield rows.
  283. std::vector<int> sectorCursor; // From left to bottom left.
  284. sectorCursor.push_back(8);
  285. sectorCursor.push_back(9);
  286. sectorCursor.push_back(10);
  287. sectorCursor.push_back(11);
  288. sectorCursor.push_back(12);
  289. sectorCursor.push_back(7);
  290. const bool doubleWide = owner.stacksController->getActiveStack()->doubleWide();
  291. bool aboveAttackable = true, belowAttackable = true;
  292. // Exclude directions which cannot be attacked from.
  293. // Check to the left.
  294. if (myNumber%GameConstants::BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - 1))
  295. {
  296. sectorCursor[0] = -1;
  297. }
  298. // Check top left, top right as well as above for 2-hex creatures.
  299. if (myNumber/GameConstants::BFIELD_WIDTH == 0)
  300. {
  301. sectorCursor[1] = -1;
  302. sectorCursor[2] = -1;
  303. aboveAttackable = false;
  304. }
  305. else
  306. {
  307. if (doubleWide)
  308. {
  309. bool attackRow[4] = {true, true, true, true};
  310. if (myNumber%GameConstants::BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH - 2 + zigzagCorrection))
  311. attackRow[0] = false;
  312. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  313. attackRow[1] = false;
  314. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH + zigzagCorrection))
  315. attackRow[2] = false;
  316. if (myNumber%GameConstants::BFIELD_WIDTH >= GameConstants::BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH + 1 + zigzagCorrection))
  317. attackRow[3] = false;
  318. if (!(attackRow[0] && attackRow[1]))
  319. sectorCursor[1] = -1;
  320. if (!(attackRow[1] && attackRow[2]))
  321. aboveAttackable = false;
  322. if (!(attackRow[2] && attackRow[3]))
  323. sectorCursor[2] = -1;
  324. }
  325. else
  326. {
  327. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  328. sectorCursor[1] = -1;
  329. if (!vstd::contains(occupyableHexes, myNumber - GameConstants::BFIELD_WIDTH + zigzagCorrection))
  330. sectorCursor[2] = -1;
  331. }
  332. }
  333. // Check to the right.
  334. if (myNumber%GameConstants::BFIELD_WIDTH >= GameConstants::BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + 1))
  335. {
  336. sectorCursor[3] = -1;
  337. }
  338. // Check bottom right, bottom left as well as below for 2-hex creatures.
  339. if (myNumber/GameConstants::BFIELD_WIDTH == GameConstants::BFIELD_HEIGHT - 1)
  340. {
  341. sectorCursor[4] = -1;
  342. sectorCursor[5] = -1;
  343. belowAttackable = false;
  344. }
  345. else
  346. {
  347. if (doubleWide)
  348. {
  349. bool attackRow[4] = {true, true, true, true};
  350. if (myNumber%GameConstants::BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH - 2 + zigzagCorrection))
  351. attackRow[0] = false;
  352. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  353. attackRow[1] = false;
  354. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH + zigzagCorrection))
  355. attackRow[2] = false;
  356. if (myNumber%GameConstants::BFIELD_WIDTH >= GameConstants::BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH + 1 + zigzagCorrection))
  357. attackRow[3] = false;
  358. if (!(attackRow[0] && attackRow[1]))
  359. sectorCursor[5] = -1;
  360. if (!(attackRow[1] && attackRow[2]))
  361. belowAttackable = false;
  362. if (!(attackRow[2] && attackRow[3]))
  363. sectorCursor[4] = -1;
  364. }
  365. else
  366. {
  367. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH + zigzagCorrection))
  368. sectorCursor[4] = -1;
  369. if (!vstd::contains(occupyableHexes, myNumber + GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection))
  370. sectorCursor[5] = -1;
  371. }
  372. }
  373. // Determine index from sector.
  374. int cursorIndex;
  375. if (doubleWide)
  376. {
  377. sectorCursor.insert(sectorCursor.begin() + 5, belowAttackable ? 13 : -1);
  378. sectorCursor.insert(sectorCursor.begin() + 2, aboveAttackable ? 14 : -1);
  379. if (sector < 1.5)
  380. cursorIndex = static_cast<int>(sector);
  381. else if (sector >= 1.5 && sector < 2.5)
  382. cursorIndex = 2;
  383. else if (sector >= 2.5 && sector < 4.5)
  384. cursorIndex = (int) sector + 1;
  385. else if (sector >= 4.5 && sector < 5.5)
  386. cursorIndex = 6;
  387. else
  388. cursorIndex = (int) sector + 2;
  389. }
  390. else
  391. {
  392. cursorIndex = static_cast<int>(sector);
  393. }
  394. // Generally should NEVER happen, but to avoid the possibility of having endless loop below... [#1016]
  395. if (!vstd::contains_if (sectorCursor, [](int sc) { return sc != -1; }))
  396. {
  397. logGlobal->error("Error: for hex %d cannot find a hex to attack from!", myNumber);
  398. attackingHex = -1;
  399. return;
  400. }
  401. // Find the closest direction attackable, starting with the right one.
  402. // FIXME: Is this really how the original H3 client does it?
  403. int i = 0;
  404. while (sectorCursor[(cursorIndex + i)%sectorCursor.size()] == -1) //Why hast thou forsaken me?
  405. i = i <= 0 ? 1 - i : -i; // 0, 1, -1, 2, -2, 3, -3 etc..
  406. int index = (cursorIndex + i)%sectorCursor.size(); //hopefully we get elements from sectorCursor
  407. cursor->changeGraphic(ECursor::COMBAT, sectorCursor[index]);
  408. switch (index)
  409. {
  410. case 0:
  411. attackingHex = myNumber - 1; //left
  412. break;
  413. case 1:
  414. attackingHex = myNumber - GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection; //top left
  415. break;
  416. case 2:
  417. attackingHex = myNumber - GameConstants::BFIELD_WIDTH + zigzagCorrection; //top right
  418. break;
  419. case 3:
  420. attackingHex = myNumber + 1; //right
  421. break;
  422. case 4:
  423. attackingHex = myNumber + GameConstants::BFIELD_WIDTH + zigzagCorrection; //bottom right
  424. break;
  425. case 5:
  426. attackingHex = myNumber + GameConstants::BFIELD_WIDTH - 1 + zigzagCorrection; //bottom left
  427. break;
  428. }
  429. BattleHex hex(attackingHex);
  430. if (!hex.isValid())
  431. attackingHex = -1;
  432. }
  433. BattleHex BattleFieldController::fromWhichHexAttack(BattleHex myNumber)
  434. {
  435. //TODO far too much repeating code
  436. BattleHex destHex;
  437. switch(CCS->curh->frame)
  438. {
  439. case 12: //from bottom right
  440. {
  441. bool doubleWide = owner.stacksController->getActiveStack()->doubleWide();
  442. destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ) +
  443. (owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
  444. if(vstd::contains(occupyableHexes, destHex))
  445. return destHex;
  446. else if(owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  447. {
  448. if (vstd::contains(occupyableHexes, destHex+1))
  449. return destHex+1;
  450. }
  451. else //if we are defender
  452. {
  453. if(vstd::contains(occupyableHexes, destHex-1))
  454. return destHex-1;
  455. }
  456. break;
  457. }
  458. case 7: //from bottom left
  459. {
  460. destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH-1 : GameConstants::BFIELD_WIDTH );
  461. if (vstd::contains(occupyableHexes, destHex))
  462. return destHex;
  463. else if(owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  464. {
  465. if(vstd::contains(occupyableHexes, destHex+1))
  466. return destHex+1;
  467. }
  468. else //we are defender
  469. {
  470. if(vstd::contains(occupyableHexes, destHex-1))
  471. return destHex-1;
  472. }
  473. break;
  474. }
  475. case 8: //from left
  476. {
  477. if(owner.stacksController->getActiveStack()->doubleWide() && owner.stacksController->getActiveStack()->side == BattleSide::DEFENDER)
  478. {
  479. std::vector<BattleHex> acc = owner.curInt->cb->battleGetAvailableHexes(owner.stacksController->getActiveStack());
  480. if (vstd::contains(acc, myNumber))
  481. return myNumber - 1;
  482. else
  483. return myNumber - 2;
  484. }
  485. else
  486. {
  487. return myNumber - 1;
  488. }
  489. break;
  490. }
  491. case 9: //from top left
  492. {
  493. destHex = myNumber - ((myNumber/GameConstants::BFIELD_WIDTH) % 2 ? GameConstants::BFIELD_WIDTH + 1 : GameConstants::BFIELD_WIDTH);
  494. if(vstd::contains(occupyableHexes, destHex))
  495. return destHex;
  496. else if(owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  497. {
  498. if(vstd::contains(occupyableHexes, destHex+1))
  499. return destHex+1;
  500. }
  501. else //if we are defender
  502. {
  503. if(vstd::contains(occupyableHexes, destHex-1))
  504. return destHex-1;
  505. }
  506. break;
  507. }
  508. case 10: //from top right
  509. {
  510. bool doubleWide = owner.stacksController->getActiveStack()->doubleWide();
  511. destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ) +
  512. (owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
  513. if(vstd::contains(occupyableHexes, destHex))
  514. return destHex;
  515. else if(owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  516. {
  517. if(vstd::contains(occupyableHexes, destHex+1))
  518. return destHex+1;
  519. }
  520. else //if we are defender
  521. {
  522. if(vstd::contains(occupyableHexes, destHex-1))
  523. return destHex-1;
  524. }
  525. break;
  526. }
  527. case 11: //from right
  528. {
  529. if(owner.stacksController->getActiveStack()->doubleWide() && owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  530. {
  531. std::vector<BattleHex> acc = owner.curInt->cb->battleGetAvailableHexes(owner.stacksController->getActiveStack());
  532. if(vstd::contains(acc, myNumber))
  533. return myNumber + 1;
  534. else
  535. return myNumber + 2;
  536. }
  537. else
  538. {
  539. return myNumber + 1;
  540. }
  541. break;
  542. }
  543. case 13: //from bottom
  544. {
  545. destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 );
  546. if(vstd::contains(occupyableHexes, destHex))
  547. return destHex;
  548. else if(owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  549. {
  550. if(vstd::contains(occupyableHexes, destHex+1))
  551. return destHex+1;
  552. }
  553. else //if we are defender
  554. {
  555. if(vstd::contains(occupyableHexes, destHex-1))
  556. return destHex-1;
  557. }
  558. break;
  559. }
  560. case 14: //from top
  561. {
  562. destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 );
  563. if (vstd::contains(occupyableHexes, destHex))
  564. return destHex;
  565. else if(owner.stacksController->getActiveStack()->side == BattleSide::ATTACKER)
  566. {
  567. if(vstd::contains(occupyableHexes, destHex+1))
  568. return destHex+1;
  569. }
  570. else //if we are defender
  571. {
  572. if(vstd::contains(occupyableHexes, destHex-1))
  573. return destHex-1;
  574. }
  575. break;
  576. }
  577. }
  578. return -1;
  579. }
  580. bool BattleFieldController::isTileAttackable(const BattleHex & number) const
  581. {
  582. for (auto & elem : occupyableHexes)
  583. {
  584. if (BattleHex::mutualPosition(elem, number) != -1 || elem == number)
  585. return true;
  586. }
  587. return false;
  588. }
  589. bool BattleFieldController::stackCountOutsideHex(const BattleHex & number) const
  590. {
  591. return stackCountOutsideHexes[number];
  592. }