BattleLogic.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. #include "BattleLogic.h"
  2. #include "../../lib/BattleState.h"
  3. #include <math.h>
  4. #include <boost/lexical_cast.hpp>
  5. #include <boost/lambda/lambda.hpp>
  6. #include <boost/lambda/bind.hpp>
  7. #include <boost/lambda/if.hpp>
  8. #include <boost/foreach.hpp>
  9. #ifdef _WIN32
  10. #define WIN32_LEAN_AND_MEAN //excludes rarely used stuff from windows headers - delete this line if something is missing
  11. #include <windows.h>
  12. HANDLE handleIn;
  13. HANDLE handleOut;
  14. #endif
  15. using namespace geniusai::BattleAI;
  16. using namespace boost::lambda;
  17. using namespace std;
  18. #if _MSC_VER >= 1600
  19. #define bind boost::lambda::bind
  20. #endif
  21. /*
  22. ui8 side; //who made this action: false - left, true - right player
  23. ui32 stackNumber;//stack ID, -1 left hero, -2 right hero,
  24. ui8 actionType; //
  25. 0 = Cancel BattleAction
  26. 1 = Hero cast a spell
  27. 2 = Walk
  28. 3 = Defend
  29. 4 = Retreat from the battle
  30. 5 = Surrender
  31. 6 = Walk and Attack
  32. 7 = Shoot
  33. 8 = Wait
  34. 9 = Catapult
  35. 10 = Monster casts a spell (i.e. Faerie Dragons)
  36. ui16 destinationTile;
  37. si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
  38. */
  39. /**
  40. * Implementation of CBattleLogic class.
  41. */
  42. CBattleLogic::CBattleLogic(CCallback *cb, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) :
  43. m_iCurrentTurn(-2),
  44. m_bIsAttacker(!side),
  45. m_cb(cb),
  46. m_army1(army1),
  47. m_army2(army2),
  48. m_tile(tile),
  49. m_hero1(hero1),
  50. m_hero2(hero2),
  51. m_side(side)
  52. {
  53. const int max_enemy_creatures = 12;
  54. m_statMaxDamage.reserve(max_enemy_creatures);
  55. m_statMinDamage.reserve(max_enemy_creatures);
  56. m_statMaxSpeed.reserve(max_enemy_creatures);
  57. m_statDistance.reserve(max_enemy_creatures);
  58. m_statDistanceFromShooters.reserve(max_enemy_creatures);
  59. m_statHitPoints.reserve(max_enemy_creatures);
  60. }
  61. CBattleLogic::~CBattleLogic()
  62. {}
  63. void CBattleLogic::SetCurrentTurn(int turn)
  64. {
  65. m_iCurrentTurn = turn;
  66. }
  67. void CBattleLogic::MakeStatistics(int currentCreatureId)
  68. {
  69. typedef std::vector<const CStack*> vector_stacks;
  70. vector_stacks allStacks = m_cb->battleGetStacks();
  71. const CStack *currentStack = m_cb->battleGetStackByID(currentCreatureId);
  72. /*
  73. // find all creatures belong to the enemy
  74. std::for_each(allStacks.begin(), allStacks.end(),
  75. if_(bind<ui8>(&CStack::attackerOwned, bind<CStack>(&map_stacks::value_type::second, _1)) == m_bIsAttacker)
  76. [
  77. var(enemy)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  78. ret<CStack>(bind<CStack>(&map_stacks::value_type::second, _1))
  79. ]
  80. );
  81. // fill other containers
  82. // max damage
  83. std::for_each(enemy.begin(), enemy.end(),
  84. var(m_statMaxDamage)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  85. ret<int>(bind<int>(&CCreature::damageMax, bind<CCreature*>(&CStack::creature,
  86. bind<CStack>(&map_stacks::value_type::second, _1))))
  87. );
  88. // min damage
  89. std::for_each(enemy.begin(), enemy.end(),
  90. var(m_statMinDamage)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  91. ret<int>(bind<int>(&CCreature::damageMax, bind<CCreature*>(&CStack::creature,
  92. bind<CStack>(&map_stacks::value_type::second, _1))))
  93. );
  94. */
  95. m_statMaxDamage.clear();
  96. m_statMinDamage.clear();
  97. m_statHitPoints.clear();
  98. m_statMaxSpeed.clear();
  99. m_statDistanceFromShooters.clear();
  100. m_statDistance.clear();
  101. m_statDistance.clear();
  102. m_statCasualties.clear();
  103. int totalEnemyDamage = 0;
  104. int totalEnemyHitPoints = 0;
  105. int totalDamage = 0;
  106. int totalHitPoints = 0;
  107. BOOST_FOREACH(const CStack *st, allStacks)
  108. {
  109. const int stackHP = st->valOfBonuses(Bonus::STACK_HEALTH);
  110. if ((st->attackerOwned != 0) != m_bIsAttacker)
  111. {
  112. int id = st->ID;
  113. if (st->count < 1)
  114. {
  115. continue;
  116. }
  117. // make stats
  118. int hitPoints = st->count * stackHP - (stackHP - st->firstHPleft);
  119. m_statMaxDamage.push_back(std::pair<int, int>(id, st->getMaxDamage() * st->count));
  120. m_statMinDamage.push_back(std::pair<int, int>(id, st->getMinDamage() * st->count));
  121. m_statHitPoints.push_back(std::pair<int, int>(id, hitPoints));
  122. m_statMaxSpeed.push_back(std::pair<int, int>(id, stackHP));
  123. totalEnemyDamage += (st->getCreature()->damageMax + st->getCreature()->damageMin) * st->count / 2;
  124. totalEnemyHitPoints += hitPoints;
  125. // calculate casualties
  126. SCreatureCasualties cs;
  127. // hp * amount - damage * ( (att - def)>=0 )
  128. // hit poionts
  129. assert(hitPoints >= 0 && "CGeniusAI - creature cannot have hit points less than zero");
  130. //CGHeroInstance *attackerHero = (m_side)? m_hero1 : m_hero2;
  131. //CGHeroInstance *defendingHero = (m_side)? m_hero2 : m_hero1;
  132. int attackDefenseBonus = currentStack->Attack() - st->Defense();
  133. float damageFactor = 1.0f;
  134. if(attackDefenseBonus < 0) //decreasing dmg
  135. {
  136. if(0.02f * (-attackDefenseBonus) > 0.3f)
  137. {
  138. damageFactor += -0.3f;
  139. }
  140. else
  141. {
  142. damageFactor += 0.02f * attackDefenseBonus;
  143. }
  144. }
  145. else //increasing dmg
  146. {
  147. if(0.05f * attackDefenseBonus > 4.0f)
  148. {
  149. damageFactor += 4.0f;
  150. }
  151. else
  152. {
  153. damageFactor += 0.05f * attackDefenseBonus;
  154. }
  155. }
  156. cs.damage_max = (int)(currentStack->getMaxDamage() * currentStack->count * damageFactor);
  157. if (cs.damage_max > hitPoints)
  158. {
  159. cs.damage_max = hitPoints;
  160. }
  161. cs.damage_min = (int)(currentStack->getMinDamage() * currentStack->count * damageFactor);
  162. if (cs.damage_min > hitPoints)
  163. {
  164. cs.damage_min = hitPoints;
  165. }
  166. cs.amount_max = cs.damage_max / stackHP;
  167. cs.amount_min = cs.damage_min / stackHP;
  168. cs.leftHitPoints_for_max = (hitPoints - cs.damage_max) % stackHP;
  169. cs.leftHitPoint_for_min = (hitPoints - cs.damage_min) % stackHP;
  170. m_statCasualties.push_back(std::pair<int, SCreatureCasualties>(id, cs));
  171. if (st->getCreature()->isShooting() && st->shots > 0)
  172. {
  173. m_statDistanceFromShooters.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
  174. }
  175. if (currentStack->hasBonusOfType(Bonus::FLYING) || (currentStack->getCreature()->isShooting() && currentStack->shots > 0))
  176. {
  177. m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
  178. }
  179. else
  180. {
  181. m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetDistanceWithObstacles(currentStack->position, st->position)));
  182. }
  183. }
  184. else
  185. {
  186. if (st->count < 1)
  187. {
  188. continue;
  189. }
  190. int hitPoints = st->count * stackHP - (stackHP - st->firstHPleft);
  191. totalDamage += (st->getMaxDamage() + st->getMinDamage()) * st->count / 2;
  192. totalHitPoints += hitPoints;
  193. }
  194. }
  195. if ((float)totalDamage / (float)totalEnemyDamage < 0.5f &&
  196. (float)totalHitPoints / (float)totalEnemyHitPoints < 0.5f)
  197. {
  198. m_bEnemyDominates = true;
  199. DbgBox("** EnemyDominates!");
  200. }
  201. else
  202. {
  203. m_bEnemyDominates = false;
  204. }
  205. //boost compile hack
  206. typedef int const std::pair<int, int>::* IntPtr;
  207. typedef int const SCreatureCasualties::* CreaPtr;
  208. typedef SCreatureCasualties const std::pair<int, SCreatureCasualties>::* CreaPairPtr;
  209. // sort max damage
  210. // std::sort(m_statMaxDamage.begin(), m_statMaxDamage.end(),
  211. // bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
  212. // // sort min damage
  213. // std::sort(m_statMinDamage.begin(), m_statMinDamage.end(),
  214. // bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
  215. // // sort max speed
  216. // std::sort(m_statMaxSpeed.begin(), m_statMaxSpeed.end(),
  217. // bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
  218. // // sort distance
  219. // std::sort(m_statDistance.begin(), m_statDistance.end(),
  220. // bind((IntPtr)&creature_stat::value_type::second, _1) < bind((IntPtr)&creature_stat::value_type::second, _2));
  221. // // sort distance from shooters
  222. // std::sort(m_statDistanceFromShooters.begin(), m_statDistanceFromShooters.end(),
  223. // bind((IntPtr)&creature_stat::value_type::second, _1) < bind((IntPtr)&creature_stat::value_type::second, _2));
  224. // // sort hit points
  225. // std::sort(m_statHitPoints.begin(), m_statHitPoints.end(),
  226. // bind((IntPtr)&creature_stat::value_type::second, _1) > bind((IntPtr)&creature_stat::value_type::second, _2));
  227. // // sort casualties
  228. // std::sort(m_statCasualties.begin(), m_statCasualties.end(),
  229. // bind((CreaPtr)&creature_stat_casualties::value_type::second_type::damage_max,
  230. // bind((CreaPairPtr)&creature_stat_casualties::value_type::second, _1))
  231. // >
  232. // bind((CreaPtr)&creature_stat_casualties::value_type::second_type::damage_max,
  233. // bind((CreaPairPtr)&creature_stat_casualties::value_type::second, _2)));
  234. }
  235. BattleAction CBattleLogic::MakeDecision(int stackID)
  236. {
  237. const CStack *currentStack = m_cb->battleGetStackByID(stackID);
  238. if(!currentStack->position.isValid() || currentStack->getCreature()->idNumber == 147) //turret or first aid kit
  239. {
  240. return BattleAction::makeDefend(currentStack);
  241. }
  242. MakeStatistics(stackID);
  243. list<int> creatures;
  244. int additionalInfo = 0; //?
  245. if (m_bEnemyDominates)
  246. {
  247. creatures = PerformBerserkAttack(stackID, additionalInfo);
  248. }
  249. else
  250. {
  251. creatures = PerformDefaultAction(stackID, additionalInfo);
  252. }
  253. /*std::string message("Creature will be attacked - ");
  254. message += boost::lexical_cast<std::string>(creature_to_attack);
  255. DbgBox(message.c_str());*/
  256. if (additionalInfo == -1 || creatures.empty())
  257. {
  258. // defend
  259. return BattleAction::makeDefend(currentStack);
  260. }
  261. else if (additionalInfo == -2)
  262. {
  263. return BattleAction::makeWait(currentStack);
  264. }
  265. list<int>::iterator it, eit;
  266. eit = creatures.end();
  267. for (it = creatures.begin(); it != eit; ++it)
  268. {
  269. BattleAction ba = MakeAttack(stackID, *it);
  270. if (ba.actionType != action_walk_and_attack)
  271. {
  272. continue;
  273. }
  274. else
  275. {
  276. #if defined PRINT_DEBUG
  277. PrintBattleAction(ba);
  278. #endif
  279. return ba;
  280. }
  281. }
  282. BattleAction ba = MakeAttack(stackID, *creatures.begin());
  283. return ba;
  284. }
  285. std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(const CStack *defender, const CStack *attacker)
  286. {
  287. int x = m_battleHelper.DecodeXPosition(defender->position);
  288. int y = m_battleHelper.DecodeYPosition(defender->position);
  289. bool defenderIsDW = defender->doubleWide();
  290. bool attackerIsDW = attacker->doubleWide();
  291. // TOTO: should be std::vector<int> but for debug purpose std::pair is used
  292. typedef std::pair<int, int> hexPoint;
  293. std::list<hexPoint> candidates;
  294. std::vector<int> fields;
  295. if (defenderIsDW)
  296. {
  297. if (defender->attackerOwned)
  298. {
  299. // from left side
  300. if (!(y % 2))
  301. {
  302. // up
  303. candidates.push_back(hexPoint(x - 2, y - 1));
  304. candidates.push_back(hexPoint(x - 1, y - 1));
  305. candidates.push_back(hexPoint(x, y - 1));
  306. // down
  307. candidates.push_back(hexPoint(x - 2, y + 1));
  308. candidates.push_back(hexPoint(x - 1, y + 1));
  309. candidates.push_back(hexPoint(x, y + 1));
  310. }
  311. else
  312. {
  313. // up
  314. candidates.push_back(hexPoint(x - 1, y - 1));
  315. candidates.push_back(hexPoint(x, y - 1));
  316. candidates.push_back(hexPoint(x + 1, y - 1));
  317. // down
  318. candidates.push_back(hexPoint(x - 1, y + 1));
  319. candidates.push_back(hexPoint(x, y + 1));
  320. candidates.push_back(hexPoint(x + 1, y + 1));
  321. }
  322. candidates.push_back(hexPoint(x - 2, y));
  323. candidates.push_back(hexPoint(x + 1, y));
  324. }
  325. else
  326. {
  327. // from right
  328. if (!(y % 2))
  329. {
  330. // up
  331. candidates.push_back(hexPoint(x - 1, y - 1));
  332. candidates.push_back(hexPoint(x, y - 1));
  333. candidates.push_back(hexPoint(x + 1, y - 1));
  334. // down
  335. candidates.push_back(hexPoint(x - 1, y + 1));
  336. candidates.push_back(hexPoint(x, y + 1));
  337. candidates.push_back(hexPoint(x + 1, y + 1));
  338. }
  339. else
  340. {
  341. // up
  342. candidates.push_back(hexPoint(x, y - 1));
  343. candidates.push_back(hexPoint(x + 1, y - 1));
  344. candidates.push_back(hexPoint(x + 2, y - 1));
  345. // down
  346. candidates.push_back(hexPoint(x, y + 1));
  347. candidates.push_back(hexPoint(x + 1, y + 1));
  348. candidates.push_back(hexPoint(x + 2, y + 1));
  349. }
  350. candidates.push_back(hexPoint(x - 1, y));
  351. candidates.push_back(hexPoint(x + 2, y));
  352. }
  353. }
  354. else
  355. {
  356. if (!(y % 2)) // even line
  357. {
  358. // up
  359. candidates.push_back(hexPoint(x - 1, y - 1));
  360. candidates.push_back(hexPoint(x, y - 1));
  361. // down
  362. candidates.push_back(hexPoint(x - 1, y + 1));
  363. candidates.push_back(hexPoint(x, y + 1));
  364. }
  365. else // odd line
  366. {
  367. // up
  368. candidates.push_back(hexPoint(x, y - 1));
  369. candidates.push_back(hexPoint(x + 1, y - 1));
  370. // down
  371. candidates.push_back(hexPoint(x, y + 1));
  372. candidates.push_back(hexPoint(x + 1, y + 1));
  373. }
  374. candidates.push_back(hexPoint(x + 1, y));
  375. candidates.push_back(hexPoint(x - 1, y));
  376. }
  377. // remove fields which are out of bounds or obstacles
  378. for (std::list<hexPoint>::iterator it = candidates.begin(); it != candidates.end(); ++it)
  379. {
  380. if (it->first < 1 || it->first > m_battleHelper.BattlefieldWidth ||
  381. it->second < 1 || it->second > m_battleHelper.BattlefieldHeight)
  382. {
  383. // field is out of bounds
  384. //it = candidates.erase(it);
  385. continue;
  386. }
  387. int new_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
  388. const CStack *st = m_cb->battleGetStackByPos(new_pos);
  389. if (st == NULL || st->count < 1)
  390. {
  391. if (attackerIsDW)
  392. {
  393. int tail_pos = -1;
  394. if (attacker->attackerOwned) // left side
  395. {
  396. int tail_pos_x = it->first - 1;
  397. if (tail_pos_x < 1)
  398. {
  399. continue;
  400. }
  401. tail_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
  402. }
  403. else // right side
  404. {
  405. int tail_pos_x = it->first + 1;
  406. if (tail_pos_x > m_battleHelper.BattlefieldWidth)
  407. {
  408. continue;
  409. }
  410. tail_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
  411. }
  412. assert(tail_pos >= 0 && "Error during calculation position of double wide creature");
  413. //CStack *tailStack = m_cb->battleGetStackByPos(tail_pos);
  414. if (st != NULL && st->count >= 1)
  415. {
  416. continue;
  417. }
  418. }
  419. fields.push_back(new_pos);
  420. }
  421. else if (attacker)
  422. {
  423. if (attacker->ID == st->ID)
  424. {
  425. fields.push_back(new_pos);
  426. }
  427. }
  428. //
  429. //++it;
  430. }
  431. return fields;
  432. }
  433. BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
  434. {
  435. const CStack *attackerStack = m_cb->battleGetStackByID(attackerID),
  436. *destinationStack = m_cb->battleGetStackByID(destinationID);
  437. assert(attackerStack && destinationStack);
  438. //don't attack ourselves
  439. if(destinationStack->attackerOwned == !m_side)
  440. {
  441. return BattleAction::makeDefend(attackerStack);
  442. }
  443. if (m_cb->battleCanShoot(attackerStack, destinationStack->position)) // shoot
  444. {
  445. return BattleAction::makeShotAttack(attackerStack, destinationStack);
  446. }
  447. else
  448. {
  449. // go or go&attack
  450. int dest_tile = -1;
  451. std::vector<int> av_tiles = GetAvailableHexesForAttacker(m_cb->battleGetStackByID(destinationID), m_cb->battleGetStackByID(attackerID));
  452. if (av_tiles.size() < 1)
  453. {
  454. return BattleAction::makeDefend(attackerStack);
  455. }
  456. // get the best tile - now the nearest
  457. int prev_distance = m_battleHelper.InfiniteDistance;
  458. int currentPos = m_cb->battleGetPos(attackerID);
  459. for (std::vector<int>::iterator it = av_tiles.begin(); it != av_tiles.end(); ++it)
  460. {
  461. int dist = m_battleHelper.GetDistanceWithObstacles(*it, m_cb->battleGetPos(attackerID));
  462. if (dist < prev_distance)
  463. {
  464. prev_distance = dist;
  465. dest_tile = *it;
  466. }
  467. if (*it == currentPos)
  468. {
  469. dest_tile = currentPos;
  470. break;
  471. }
  472. }
  473. std::vector<THex> fields = m_cb->battleGetAvailableHexes(m_cb->battleGetStackByID(attackerID), false);
  474. if(fields.size() == 0)
  475. {
  476. return BattleAction::makeDefend(attackerStack);
  477. }
  478. BattleAction ba;
  479. ba.side = m_side;
  480. //ba.actionType = 6; // go and attack
  481. ba.stackNumber = attackerID;
  482. ba.destinationTile = static_cast<ui16>(dest_tile);
  483. //simplified checking for possibility of attack (previous was too simplified)
  484. int destStackPos = m_cb->battleGetPos(destinationID);
  485. if(THex::mutualPosition(dest_tile, destStackPos) != -1)
  486. ba.additionalInfo = destStackPos;
  487. else if(THex::mutualPosition(dest_tile, destStackPos+1) != -1)
  488. ba.additionalInfo = destStackPos+1;
  489. else if(THex::mutualPosition(dest_tile, destStackPos-1) != -1)
  490. ba.additionalInfo = destStackPos-1;
  491. else
  492. return BattleAction::makeDefend(attackerStack);
  493. int nearest_dist = m_battleHelper.InfiniteDistance;
  494. int nearest_pos = -1;
  495. // if double wide calculate tail
  496. int tail_pos = -1;
  497. if (attackerStack->doubleWide())
  498. {
  499. int x_pos = m_battleHelper.DecodeXPosition(attackerStack->position);
  500. int y_pos = m_battleHelper.DecodeYPosition(attackerStack->position);
  501. if (attackerStack->attackerOwned)
  502. {
  503. x_pos -= 1;
  504. }
  505. else
  506. {
  507. x_pos += 1;
  508. }
  509. // if creature can perform attack without movement - do it!
  510. tail_pos = m_battleHelper.GetBattleFieldPosition(x_pos, y_pos);
  511. if (dest_tile == tail_pos)
  512. {
  513. ba.additionalInfo = dest_tile;
  514. ba.actionType = action_walk_and_attack;
  515. #if defined PRINT_DEBUG
  516. PrintBattleAction(ba);
  517. #endif
  518. assert(m_cb->battleGetStackByPos(ba.additionalInfo, false)); //if action is action_walk_and_attack additional info must point on enemy stack
  519. assert(m_cb->battleGetStackByPos(ba.additionalInfo, false) != attackerStack); //don't attack ourselve
  520. return ba;
  521. }
  522. }
  523. for (std::vector<THex>::const_iterator it = fields.begin(); it != fields.end(); ++it)
  524. {
  525. if (*it == dest_tile)
  526. {
  527. // attack!
  528. ba.actionType = action_walk_and_attack;
  529. #if defined PRINT_DEBUG
  530. PrintBattleAction(ba);
  531. #endif
  532. assert(m_cb->battleGetStackByPos(ba.additionalInfo)); //if action is action_walk_and_attack additional info must point on enemy stack
  533. assert(m_cb->battleGetStackByPos(ba.additionalInfo) != attackerStack); //don't attack ourselve
  534. return ba;
  535. }
  536. int d = m_battleHelper.GetDistanceWithObstacles(dest_tile, *it);
  537. if (d < nearest_dist)
  538. {
  539. nearest_dist = d;
  540. nearest_pos = *it;
  541. }
  542. }
  543. string message;
  544. message = "Attacker position X=";
  545. message += boost::lexical_cast<std::string>(m_battleHelper.DecodeXPosition(nearest_pos)) + ", Y=";
  546. message += boost::lexical_cast<std::string>(m_battleHelper.DecodeYPosition(nearest_pos));
  547. DbgBox(message.c_str());
  548. ba.actionType = action_walk;
  549. ba.destinationTile = (ui16)nearest_pos;
  550. ba.additionalInfo = -1;
  551. #if defined PRINT_DEBUG
  552. PrintBattleAction(ba);
  553. #endif
  554. return ba;
  555. }
  556. }
  557. /**
  558. * The main idea is to perform maximum casualties.
  559. */
  560. list<int> CBattleLogic::PerformBerserkAttack(int stackID, int &additionalInfo)
  561. {
  562. const CStack * c = m_cb->battleGetStackByID(stackID);
  563. // attack to make biggest damage
  564. list<int> creatures;
  565. if (!m_statCasualties.empty())
  566. {
  567. //creature_to_attack = m_statCasualties.begin()->first;
  568. creature_stat_casualties::iterator it = m_statCasualties.begin();
  569. for (; it != m_statCasualties.end(); ++it)
  570. {
  571. if (it->second.amount_min <= 0)
  572. {
  573. creatures.push_back(it->first);
  574. continue;
  575. }
  576. for (creature_stat::const_iterator it2 = m_statDistance.begin(); it2 != m_statDistance.end(); ++it2)
  577. {
  578. if (it2->first == it->first && it2->second - 1 <= c->getCreature()->valOfBonuses(Bonus::STACKS_SPEED))
  579. {
  580. creatures.push_front(it->first);
  581. }
  582. }
  583. }
  584. creatures.push_back(m_statCasualties.begin()->first);
  585. }
  586. return creatures;
  587. }
  588. list<int> CBattleLogic::PerformDefaultAction(int stackID, int &additionalInfo)
  589. {
  590. // first approach based on the statistics and weights
  591. // if this solution was fine we would develop this idea
  592. //
  593. std::map<int, int> votes;
  594. for (creature_stat::iterator it = m_statMaxDamage.begin(); it != m_statMaxDamage.end(); ++it)
  595. {
  596. votes[it->first] = 0;
  597. }
  598. votes[m_statMaxDamage.begin()->first] += m_battleHelper.GetVoteForMaxDamage();
  599. votes[m_statMinDamage.begin()->first] += m_battleHelper.GetVoteForMinDamage();
  600. if (m_statDistanceFromShooters.size())
  601. {
  602. votes[m_statDistanceFromShooters.begin()->first] += m_battleHelper.GetVoteForDistanceFromShooters();
  603. }
  604. votes[m_statDistance.begin()->first] += m_battleHelper.GetVoteForDistance();
  605. votes[m_statHitPoints.begin()->first] += m_battleHelper.GetVoteForHitPoints();
  606. votes[m_statMaxSpeed.begin()->first] += m_battleHelper.GetVoteForMaxSpeed();
  607. // get creature to attack
  608. int max_vote = 0;
  609. list<int> creatures;
  610. for (std::map<int, int>::iterator it = votes.begin(); it != votes.end(); ++it)
  611. {
  612. if (bool(m_cb->battleGetStackByID(it->first)->attackerOwned) == m_side //it's hostile stack
  613. && it->second > max_vote)
  614. {
  615. max_vote = it->second;
  616. creatures.push_front(it->first);
  617. }
  618. }
  619. additionalInfo = 0; // list contains creatures which shoud be attacked
  620. return creatures;
  621. }
  622. void CBattleLogic::PrintBattleAction(const BattleAction &action) // for debug purpose
  623. {
  624. std::string message("Battle action \n");
  625. message += "\taction type - ";
  626. switch (action.actionType)
  627. {
  628. case 0:
  629. message += "Cancel BattleAction\n";
  630. break;
  631. case 1:
  632. message += "Hero cast a spell\n";
  633. break;
  634. case 2:
  635. message += "Walk\n";
  636. break;
  637. case 3:
  638. message += "Defend\n";
  639. break;
  640. case 4:
  641. message += "Retreat from the battle\n";
  642. break;
  643. case 5:
  644. message += "Surrender\n";
  645. break;
  646. case 6:
  647. message += "Walk and Attack\n";
  648. break;
  649. case 7:
  650. message += "Shoot\n";
  651. break;
  652. case 8:
  653. message += "Wait\n";
  654. break;
  655. case 9:
  656. message += "Catapult\n";
  657. break;
  658. case 10:
  659. message += "Monster casts a spell\n";
  660. break;
  661. }
  662. message += "\tDestination tile: X = ";
  663. message += boost::lexical_cast<std::string>(m_battleHelper.DecodeXPosition(action.destinationTile));
  664. message += ", Y = " + boost::lexical_cast<std::string>(m_battleHelper.DecodeYPosition(action.destinationTile));
  665. message += "\nAdditional info: ";
  666. if (action.actionType == 6)// || action.actionType == 7)
  667. {
  668. message += "stack - " + boost::lexical_cast<std::string>(m_battleHelper.DecodeXPosition(action.additionalInfo));
  669. message += ", " + boost::lexical_cast<std::string>(m_battleHelper.DecodeYPosition(action.additionalInfo));
  670. message += ", creature - ";
  671. const CStack *c = m_cb->battleGetStackByPos(action.additionalInfo);
  672. if (c && c->getCreature())
  673. {
  674. message += c->getCreature()->nameRef;
  675. }
  676. else
  677. {
  678. message += "NULL";
  679. }
  680. }
  681. else
  682. {
  683. message += boost::lexical_cast<std::string>(action.additionalInfo);
  684. }
  685. #ifdef _WIN32
  686. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  687. CONSOLE_SCREEN_BUFFER_INFO csbi;
  688. GetConsoleScreenBufferInfo(hConsole, &csbi);
  689. SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
  690. #else
  691. std::string color;
  692. color = "\x1b[1;40;32m";
  693. std::cout << color;
  694. #endif
  695. std::cout << message.c_str() << std::endl;
  696. #ifdef _WIN32
  697. SetConsoleTextAttribute(hConsole, csbi.wAttributes);
  698. #else
  699. color = "\x1b[0m";
  700. std::cout << color;
  701. #endif
  702. }