PotentialTargets.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /*
  2. * PotentialTargets.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 "PotentialTargets.h"
  12. #include "../../lib/CStack.h"//todo: remove
  13. PotentialTargets::PotentialTargets(const battle::Unit * attacker, const HypotheticBattle & state)
  14. {
  15. auto attackerInfo = state.battleGetUnitByID(attacker->unitId());
  16. auto reachability = state.getReachability(attackerInfo);
  17. auto avHexes = state.battleGetAvailableHexes(reachability, attackerInfo);
  18. //FIXME: this should part of battleGetAvailableHexes
  19. bool forceTarget = false;
  20. const battle::Unit * forcedTarget = nullptr;
  21. BattleHex forcedHex;
  22. if(attackerInfo->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE))
  23. {
  24. forceTarget = true;
  25. auto nearest = state.getNearestStack(attackerInfo);
  26. if(nearest.first != nullptr)
  27. {
  28. forcedTarget = nearest.first;
  29. forcedHex = nearest.second;
  30. }
  31. }
  32. auto aliveUnits = state.battleGetUnitsIf([=](const battle::Unit * unit)
  33. {
  34. return unit->isValidTarget() && unit->unitId() != attackerInfo->unitId();
  35. });
  36. for(auto defender : aliveUnits)
  37. {
  38. if(!forceTarget && !state.battleMatchOwner(attackerInfo, defender))
  39. continue;
  40. auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility
  41. {
  42. auto bai = BattleAttackInfo(attackerInfo, defender, shooting);
  43. if(hex.isValid() && !shooting)
  44. bai.chargedFields = reachability.distances[hex];
  45. return AttackPossibility::evaluate(bai, hex, state);
  46. };
  47. if(forceTarget)
  48. {
  49. if(forcedTarget && defender->unitId() == forcedTarget->unitId())
  50. possibleAttacks.push_back(GenerateAttackInfo(false, forcedHex));
  51. else
  52. unreachableEnemies.push_back(defender);
  53. }
  54. else if(state.battleCanShoot(attackerInfo, defender->getPosition()))
  55. {
  56. possibleAttacks.push_back(GenerateAttackInfo(true, BattleHex::INVALID));
  57. }
  58. else
  59. {
  60. for(BattleHex hex : avHexes)
  61. {
  62. if(!CStack::isMeleeAttackPossible(attackerInfo, defender, hex))
  63. continue;
  64. auto bai = GenerateAttackInfo(false, hex);
  65. if(!bai.affectedUnits.empty())
  66. possibleAttacks.push_back(bai);
  67. }
  68. if(!vstd::contains_if(possibleAttacks, [=](const AttackPossibility & pa) { return pa.attack.defender->unitId() == defender->unitId(); }))
  69. unreachableEnemies.push_back(defender);
  70. }
  71. }
  72. boost::sort(possibleAttacks, [](const AttackPossibility & lhs, const AttackPossibility & rhs) -> bool
  73. {
  74. return lhs.damageDiff() > rhs.damageDiff();
  75. });
  76. if (!possibleAttacks.empty())
  77. {
  78. auto & bestAp = possibleAttacks[0];
  79. logGlobal->info("Battle AI best: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
  80. bestAp.attack.attacker->unitType()->identifier,
  81. state.battleGetUnitByPos(bestAp.dest)->unitType()->identifier,
  82. (int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
  83. bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);
  84. }
  85. }
  86. int64_t PotentialTargets::bestActionValue() const
  87. {
  88. if(possibleAttacks.empty())
  89. return 0;
  90. return bestAction().attackValue();
  91. }
  92. const AttackPossibility & PotentialTargets::bestAction() const
  93. {
  94. if(possibleAttacks.empty())
  95. throw std::runtime_error("No best action, since we don't have any actions");
  96. return possibleAttacks.front();
  97. }