CBattleFieldController.cpp 21 KB

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